1use std::fmt::Write;
14
15use crate::ast::{AspectOperator, BinaryTemporalOp, ModalDomain, QuantifierKind, TemporalOperator, Term, VoiceOperator};
16use logicaffeine_base::Interner;
17use crate::registry::SymbolRegistry;
18use crate::token::TokenType;
19
20pub trait LogicFormatter {
22 fn quantifier(&self, kind: &QuantifierKind, var: &str, body: &str) -> String {
24 let sym = match kind {
25 QuantifierKind::Universal => self.universal(),
26 QuantifierKind::Existential => self.existential(),
27 QuantifierKind::Most => "MOST ".to_string(),
28 QuantifierKind::Few => "FEW ".to_string(),
29 QuantifierKind::Many => "MANY ".to_string(),
30 QuantifierKind::Cardinal(n) => self.cardinal(*n),
31 QuantifierKind::AtLeast(n) => self.at_least(*n),
32 QuantifierKind::AtMost(n) => self.at_most(*n),
33 QuantifierKind::Generic => "Gen ".to_string(),
34 };
35 format!("{}{}({})", sym, var, body)
36 }
37
38 fn universal(&self) -> String;
39 fn existential(&self) -> String;
40 fn cardinal(&self, n: u32) -> String;
41 fn at_least(&self, n: u32) -> String;
42 fn at_most(&self, n: u32) -> String;
43
44 fn binary_op(&self, op: &TokenType, left: &str, right: &str) -> String {
46 match op {
47 TokenType::And => format!("({} {} {})", left, self.and(), right),
48 TokenType::Or => format!("({} {} {})", left, self.or(), right),
49 TokenType::If | TokenType::Implies => format!("({} {} {})", left, self.implies(), right),
50 TokenType::Iff => format!("({} {} {})", left, self.iff(), right),
51 _ => String::new(),
52 }
53 }
54
55 fn and(&self) -> &'static str;
56 fn or(&self) -> &'static str;
57 fn implies(&self) -> &'static str;
58 fn iff(&self) -> &'static str;
59
60 fn unary_op(&self, op: &TokenType, operand: &str) -> String {
62 match op {
63 TokenType::Not => format!("{}{}", self.not(), operand),
64 _ => String::new(),
65 }
66 }
67
68 fn not(&self) -> &'static str;
69
70 fn identity(&self) -> &'static str {
72 " = "
73 }
74
75 fn wrap_identity(&self) -> bool {
77 false
78 }
79
80 fn modal(&self, domain: ModalDomain, force: f32, body: &str) -> String {
82 let sym = match domain {
83 ModalDomain::Alethic if force > 0.0 && force <= 0.5 => self.possibility(),
84 ModalDomain::Alethic => self.necessity(),
85 ModalDomain::Deontic if force <= 0.5 => "P",
86 ModalDomain::Deontic => "O",
87 ModalDomain::Temporal => "Temporal",
88 };
89 format!("{}_{{{:.1}}} {}", sym, force, body)
90 }
91
92 fn necessity(&self) -> &'static str;
93 fn possibility(&self) -> &'static str;
94
95 fn temporal(&self, op: &TemporalOperator, body: &str) -> String {
97 let sym = match op {
98 TemporalOperator::Past => self.past(),
99 TemporalOperator::Future => self.future(),
100 TemporalOperator::Always => "G",
101 TemporalOperator::Eventually => "F",
102 TemporalOperator::Next => "X",
103 };
104 format!("{}({})", sym, body)
105 }
106
107 fn past(&self) -> &'static str;
108 fn future(&self) -> &'static str;
109
110 fn temporal_binary(&self, op: &BinaryTemporalOp, left: &str, right: &str) -> String {
112 let sym = match op {
113 BinaryTemporalOp::Until => "U",
114 BinaryTemporalOp::Release => "R",
115 BinaryTemporalOp::WeakUntil => "W",
116 };
117 format!("({} {} {})", left, sym, right)
118 }
119
120 fn aspectual(&self, op: &AspectOperator, body: &str) -> String {
122 let sym = match op {
123 AspectOperator::Progressive => self.progressive(),
124 AspectOperator::Perfect => self.perfect(),
125 AspectOperator::Habitual => self.habitual(),
126 AspectOperator::Iterative => self.iterative(),
127 };
128 format!("{}({})", sym, body)
129 }
130
131 fn progressive(&self) -> &'static str;
132 fn perfect(&self) -> &'static str;
133 fn habitual(&self) -> &'static str;
134 fn iterative(&self) -> &'static str;
135
136 fn voice(&self, op: &VoiceOperator, body: &str) -> String {
138 let sym = match op {
139 VoiceOperator::Passive => self.passive(),
140 };
141 format!("{}({})", sym, body)
142 }
143
144 fn passive(&self) -> &'static str;
145
146 fn lambda(&self, var: &str, body: &str) -> String;
148
149 fn counterfactual(&self, antecedent: &str, consequent: &str) -> String;
151
152 fn superlative(&self, comp: &str, domain: &str, subject: &str) -> String;
154
155 fn event_quantifier(&self, pred: &str, adverbs: &[String]) -> String {
157 if adverbs.is_empty() {
158 format!("{}e({})", self.existential(), pred)
159 } else {
160 let conj = self.and();
161 format!(
162 "{}e({} {} {})",
163 self.existential(),
164 pred,
165 conj,
166 adverbs.join(&format!(" {} ", conj))
167 )
168 }
169 }
170
171 fn categorical_all(&self) -> &'static str;
173 fn categorical_no(&self) -> &'static str;
174 fn categorical_some(&self) -> &'static str;
175 fn categorical_not(&self) -> &'static str;
176
177 fn sanitize(&self, s: &str) -> String {
179 s.to_string()
180 }
181
182 fn use_simple_events(&self) -> bool {
184 false
185 }
186
187 fn use_full_names(&self) -> bool {
189 false
190 }
191
192 fn preserve_case(&self) -> bool {
194 false
195 }
196
197 fn include_world_arguments(&self) -> bool {
199 false
200 }
201
202 fn write_comparative<W: Write>(
205 &self,
206 w: &mut W,
207 adjective: &str,
208 subject: &str,
209 object: &str,
210 difference: Option<&str>,
211 ) -> std::fmt::Result {
212 if let Some(diff) = difference {
213 write!(w, "{}er({}, {}, {})", adjective, subject, object, diff)
214 } else {
215 write!(w, "{}er({}, {})", adjective, subject, object)
216 }
217 }
218
219 fn write_predicate<W: Write>(
222 &self,
223 w: &mut W,
224 name: &str,
225 args: &[Term],
226 registry: &mut SymbolRegistry,
227 interner: &Interner,
228 ) -> std::fmt::Result {
229 write!(w, "{}(", self.sanitize(name))?;
230 for (i, arg) in args.iter().enumerate() {
231 if i > 0 {
232 write!(w, ", ")?;
233 }
234 if self.use_full_names() {
235 arg.write_to_full(w, registry, interner)?;
236 } else {
237 arg.write_to(w, registry, interner)?;
238 }
239 }
240 write!(w, ")")
241 }
242}
243
244pub struct UnicodeFormatter;
245
246impl LogicFormatter for UnicodeFormatter {
247 fn universal(&self) -> String { "∀".to_string() }
248 fn existential(&self) -> String { "∃".to_string() }
249 fn cardinal(&self, n: u32) -> String { format!("∃={}.", n) }
250 fn at_least(&self, n: u32) -> String { format!("∃≥{}", n) }
251 fn at_most(&self, n: u32) -> String { format!("∃≤{}", n) }
252
253 fn and(&self) -> &'static str { "∧" }
254 fn or(&self) -> &'static str { "∨" }
255 fn implies(&self) -> &'static str { "→" }
256 fn iff(&self) -> &'static str { "↔" }
257 fn not(&self) -> &'static str { "¬" }
258
259 fn necessity(&self) -> &'static str { "□" }
260 fn possibility(&self) -> &'static str { "◇" }
261
262 fn past(&self) -> &'static str { "P" }
263 fn future(&self) -> &'static str { "F" }
264
265 fn progressive(&self) -> &'static str { "Prog" }
266 fn perfect(&self) -> &'static str { "Perf" }
267 fn habitual(&self) -> &'static str { "HAB" }
268 fn iterative(&self) -> &'static str { "ITER" }
269 fn passive(&self) -> &'static str { "Pass" }
270
271 fn lambda(&self, var: &str, body: &str) -> String {
272 format!("λ{}.{}", var, body)
273 }
274
275 fn counterfactual(&self, antecedent: &str, consequent: &str) -> String {
276 format!("({} □→ {})", antecedent, consequent)
277 }
278
279 fn superlative(&self, comp: &str, domain: &str, subject: &str) -> String {
280 format!(
281 "∀x(({}(x) ∧ x ≠ {}) → {}({}, x))",
282 domain, subject, comp, subject
283 )
284 }
285
286 fn categorical_all(&self) -> &'static str { "∀" }
287 fn categorical_no(&self) -> &'static str { "∀¬" }
288 fn categorical_some(&self) -> &'static str { "∃" }
289 fn categorical_not(&self) -> &'static str { "¬" }
290
291 fn use_full_names(&self) -> bool { true }
293}
294
295pub struct LatexFormatter;
296
297impl LogicFormatter for LatexFormatter {
298 fn universal(&self) -> String { "\\forall ".to_string() }
299 fn existential(&self) -> String { "\\exists ".to_string() }
300 fn cardinal(&self, n: u32) -> String { format!("\\exists_{{={}}} ", n) }
301 fn at_least(&self, n: u32) -> String { format!("\\exists_{{\\geq {}}} ", n) }
302 fn at_most(&self, n: u32) -> String { format!("\\exists_{{\\leq {}}} ", n) }
303
304 fn and(&self) -> &'static str { "\\cdot" }
305 fn or(&self) -> &'static str { "\\vee" }
306 fn implies(&self) -> &'static str { "\\supset" }
307 fn iff(&self) -> &'static str { "\\equiv" }
308 fn not(&self) -> &'static str { "\\sim " }
309
310 fn necessity(&self) -> &'static str { "\\Box" }
311 fn possibility(&self) -> &'static str { "\\Diamond" }
312
313 fn past(&self) -> &'static str { "\\mathsf{P}" }
314 fn future(&self) -> &'static str { "\\mathsf{F}" }
315
316 fn progressive(&self) -> &'static str { "\\mathsf{Prog}" }
317 fn perfect(&self) -> &'static str { "\\mathsf{Perf}" }
318 fn habitual(&self) -> &'static str { "\\mathsf{HAB}" }
319 fn iterative(&self) -> &'static str { "\\mathsf{ITER}" }
320 fn passive(&self) -> &'static str { "\\mathsf{Pass}" }
321
322 fn lambda(&self, var: &str, body: &str) -> String {
323 format!("\\lambda {}.{}", var, body)
324 }
325
326 fn counterfactual(&self, antecedent: &str, consequent: &str) -> String {
327 format!("({} \\boxright {})", antecedent, consequent)
328 }
329
330 fn superlative(&self, comp: &str, domain: &str, subject: &str) -> String {
331 format!(
332 "\\forall x(({}(x) \\land x \\neq {}) \\supset {}({}, x))",
333 domain, subject, comp, subject
334 )
335 }
336
337 fn categorical_all(&self) -> &'static str { "All" }
338 fn categorical_no(&self) -> &'static str { "No" }
339 fn categorical_some(&self) -> &'static str { "Some" }
340 fn categorical_not(&self) -> &'static str { "not" }
341
342 fn sanitize(&self, s: &str) -> String {
343 s.replace('_', r"\_")
344 .replace('^', r"\^{}")
345 .replace('&', r"\&")
346 .replace('%', r"\%")
347 .replace('#', r"\#")
348 .replace('$', r"\$")
349 }
350}
351
352pub struct SimpleFOLFormatter;
353
354impl LogicFormatter for SimpleFOLFormatter {
355 fn universal(&self) -> String { "∀".to_string() }
356 fn existential(&self) -> String { "∃".to_string() }
357 fn cardinal(&self, n: u32) -> String { format!("∃={}", n) }
358 fn at_least(&self, n: u32) -> String { format!("∃≥{}", n) }
359 fn at_most(&self, n: u32) -> String { format!("∃≤{}", n) }
360
361 fn and(&self) -> &'static str { "∧" }
362 fn or(&self) -> &'static str { "∨" }
363 fn implies(&self) -> &'static str { "→" }
364 fn iff(&self) -> &'static str { "↔" }
365 fn not(&self) -> &'static str { "¬" }
366
367 fn necessity(&self) -> &'static str { "□" }
368 fn possibility(&self) -> &'static str { "◇" }
369
370 fn past(&self) -> &'static str { "Past" }
371 fn future(&self) -> &'static str { "Future" }
372
373 fn progressive(&self) -> &'static str { "" }
374 fn perfect(&self) -> &'static str { "" }
375 fn habitual(&self) -> &'static str { "" }
376 fn iterative(&self) -> &'static str { "" }
377 fn passive(&self) -> &'static str { "" }
378
379 fn lambda(&self, var: &str, body: &str) -> String {
380 format!("λ{}.{}", var, body)
381 }
382
383 fn counterfactual(&self, antecedent: &str, consequent: &str) -> String {
384 format!("({} □→ {})", antecedent, consequent)
385 }
386
387 fn superlative(&self, comp: &str, domain: &str, subject: &str) -> String {
388 format!(
389 "∀x(({}(x) ∧ x ≠ {}) → {}({}, x))",
390 domain, subject, comp, subject
391 )
392 }
393
394 fn categorical_all(&self) -> &'static str { "∀" }
395 fn categorical_no(&self) -> &'static str { "∀¬" }
396 fn categorical_some(&self) -> &'static str { "∃" }
397 fn categorical_not(&self) -> &'static str { "¬" }
398
399 fn modal(&self, _domain: ModalDomain, _force: f32, body: &str) -> String {
400 body.to_string()
401 }
402
403 fn aspectual(&self, _op: &AspectOperator, body: &str) -> String {
404 body.to_string()
405 }
406
407 fn use_simple_events(&self) -> bool {
408 true
409 }
410
411 fn use_full_names(&self) -> bool {
412 true
413 }
414}
415
416pub struct KripkeFormatter;
420
421impl LogicFormatter for KripkeFormatter {
422 fn universal(&self) -> String { "ForAll ".to_string() }
423 fn existential(&self) -> String { "Exists ".to_string() }
424 fn cardinal(&self, n: u32) -> String { format!("Exists={} ", n) }
425 fn at_least(&self, n: u32) -> String { format!("Exists>={} ", n) }
426 fn at_most(&self, n: u32) -> String { format!("Exists<={} ", n) }
427
428 fn and(&self) -> &'static str { " And " }
429 fn or(&self) -> &'static str { " Or " }
430 fn implies(&self) -> &'static str { " Implies " }
431 fn iff(&self) -> &'static str { " Iff " }
432 fn not(&self) -> &'static str { "Not " }
433
434 fn necessity(&self) -> &'static str { "Box" }
435 fn possibility(&self) -> &'static str { "Diamond" }
436
437 fn past(&self) -> &'static str { "Past" }
438 fn future(&self) -> &'static str { "Future" }
439
440 fn progressive(&self) -> &'static str { "Prog" }
441 fn perfect(&self) -> &'static str { "Perf" }
442 fn habitual(&self) -> &'static str { "HAB" }
443 fn iterative(&self) -> &'static str { "ITER" }
444 fn passive(&self) -> &'static str { "Pass" }
445
446 fn lambda(&self, var: &str, body: &str) -> String {
447 format!("Lambda {}.{}", var, body)
448 }
449
450 fn counterfactual(&self, antecedent: &str, consequent: &str) -> String {
451 format!("({} Counterfactual {})", antecedent, consequent)
452 }
453
454 fn superlative(&self, comp: &str, domain: &str, subject: &str) -> String {
455 format!(
456 "ForAll x(({}(x) And x != {}) Implies {}({}, x))",
457 domain, subject, comp, subject
458 )
459 }
460
461 fn categorical_all(&self) -> &'static str { "ForAll" }
462 fn categorical_no(&self) -> &'static str { "ForAll Not" }
463 fn categorical_some(&self) -> &'static str { "Exists" }
464 fn categorical_not(&self) -> &'static str { "Not" }
465
466 fn modal(&self, _domain: ModalDomain, _force: f32, body: &str) -> String {
467 body.to_string()
469 }
470
471 fn use_full_names(&self) -> bool { true }
472
473 fn include_world_arguments(&self) -> bool { true }
474}
475
476pub struct RustFormatter;
479
480impl LogicFormatter for RustFormatter {
481 fn and(&self) -> &'static str { "&&" }
483 fn or(&self) -> &'static str { "||" }
484 fn not(&self) -> &'static str { "!" }
485 fn implies(&self) -> &'static str { "||" } fn iff(&self) -> &'static str { "==" }
487 fn identity(&self) -> &'static str { " == " } fn wrap_identity(&self) -> bool { true } fn use_full_names(&self) -> bool { true }
492 fn preserve_case(&self) -> bool { true } fn universal(&self) -> String { "/* ∀ */".to_string() }
496 fn existential(&self) -> String { "/* ∃ */".to_string() }
497 fn cardinal(&self, n: u32) -> String { format!("/* ∃={} */", n) }
498 fn at_least(&self, n: u32) -> String { format!("/* ∃≥{} */", n) }
499 fn at_most(&self, n: u32) -> String { format!("/* ∃≤{} */", n) }
500
501 fn necessity(&self) -> &'static str { "" }
503 fn possibility(&self) -> &'static str { "" }
504 fn past(&self) -> &'static str { "" }
505 fn future(&self) -> &'static str { "" }
506 fn progressive(&self) -> &'static str { "" }
507 fn perfect(&self) -> &'static str { "" }
508 fn habitual(&self) -> &'static str { "" }
509 fn iterative(&self) -> &'static str { "" }
510 fn passive(&self) -> &'static str { "" }
511 fn categorical_all(&self) -> &'static str { "" }
512 fn categorical_no(&self) -> &'static str { "" }
513 fn categorical_some(&self) -> &'static str { "" }
514 fn categorical_not(&self) -> &'static str { "" }
515
516 fn lambda(&self, var: &str, body: &str) -> String {
517 format!("|{}| {{ {} }}", var, body)
518 }
519
520 fn counterfactual(&self, a: &str, c: &str) -> String {
521 format!("/* if {} then {} */", a, c)
522 }
523
524 fn superlative(&self, _: &str, _: &str, _: &str) -> String {
525 "/* superlative */".to_string()
526 }
527
528 fn write_comparative<W: Write>(
530 &self,
531 w: &mut W,
532 adjective: &str,
533 subject: &str,
534 object: &str,
535 _difference: Option<&str>,
536 ) -> std::fmt::Result {
537 let adj_lower = adjective.to_lowercase();
538 match adj_lower.as_str() {
539 "great" | "big" | "large" | "tall" | "old" | "high" | "more" | "greater" => {
540 write!(w, "({} > {})", subject, object)
541 }
542 "small" | "little" | "short" | "young" | "low" | "less" | "fewer" => {
543 write!(w, "({} < {})", subject, object)
544 }
545 _ => write!(w, "({} > {})", subject, object) }
547 }
548
549 fn unary_op(&self, op: &TokenType, operand: &str) -> String {
551 match op {
552 TokenType::Not => format!("(!{})", operand),
553 _ => format!("/* unknown unary */({})", operand),
554 }
555 }
556
557 fn binary_op(&self, op: &TokenType, left: &str, right: &str) -> String {
559 match op {
560 TokenType::If | TokenType::Implies | TokenType::Then => format!("(!({}) || ({}))", left, right),
561 TokenType::And => format!("({} && {})", left, right),
562 TokenType::Or => format!("({} || {})", left, right),
563 TokenType::Iff => format!("({} == {})", left, right),
564 _ => "/* unknown op */".to_string(),
565 }
566 }
567
568 fn write_predicate<W: Write>(
570 &self,
571 w: &mut W,
572 name: &str,
573 args: &[Term],
574 _registry: &mut SymbolRegistry,
575 interner: &Interner,
576 ) -> std::fmt::Result {
577 let render = |idx: usize| -> String {
579 let mut buf = String::new();
580 if let Some(arg) = args.get(idx) {
581 let _ = arg.write_to_raw(&mut buf, interner);
582 }
583 buf
584 };
585
586 match name.to_lowercase().as_str() {
587 "greater" if args.len() == 2 => write!(w, "({} > {})", render(0), render(1)),
589 "less" if args.len() == 2 => write!(w, "({} < {})", render(0), render(1)),
590 "equal" | "equals" if args.len() == 2 => write!(w, "({} == {})", render(0), render(1)),
591 "notequal" | "not_equal" if args.len() == 2 => write!(w, "({} != {})", render(0), render(1)),
592 "greaterequal" | "atleast" | "at_least" if args.len() == 2 => write!(w, "({} >= {})", render(0), render(1)),
593 "lessequal" | "atmost" | "at_most" if args.len() == 2 => write!(w, "({} <= {})", render(0), render(1)),
594
595 "positive" if args.len() == 1 => write!(w, "({} > 0)", render(0)),
597 "negative" if args.len() == 1 => write!(w, "({} < 0)", render(0)),
598 "zero" if args.len() == 1 => write!(w, "({} == 0)", render(0)),
599 "empty" if args.len() == 1 => write!(w, "{}.is_empty()", render(0)),
600
601 "in" if args.len() == 2 => write!(w, "{}.contains(&{})", render(1), render(0)),
603 "contains" if args.len() == 2 => write!(w, "{}.contains(&{})", render(0), render(1)),
604
605 _ if args.len() == 1 => write!(w, "{}.is_{}()", render(0), name.to_lowercase()),
607 _ => {
608 write!(w, "{}(", name.to_lowercase())?;
609 for i in 0..args.len() {
610 if i > 0 { write!(w, ", ")?; }
611 write!(w, "{}", render(i))?;
612 }
613 write!(w, ")")
614 }
615 }
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 #[test]
624 fn unicode_binary_operators() {
625 let f = UnicodeFormatter;
626 assert_eq!(f.binary_op(&TokenType::And, "P", "Q"), "(P ∧ Q)");
627 assert_eq!(f.binary_op(&TokenType::Or, "P", "Q"), "(P ∨ Q)");
628 assert_eq!(f.binary_op(&TokenType::If, "P", "Q"), "(P → Q)");
629 assert_eq!(f.binary_op(&TokenType::Iff, "P", "Q"), "(P ↔ Q)");
630 }
631
632 #[test]
633 fn latex_binary_operators() {
634 let f = LatexFormatter;
635 assert_eq!(f.binary_op(&TokenType::And, "P", "Q"), r"(P \cdot Q)");
636 assert_eq!(f.binary_op(&TokenType::Or, "P", "Q"), r"(P \vee Q)");
637 assert_eq!(f.binary_op(&TokenType::If, "P", "Q"), r"(P \supset Q)");
638 assert_eq!(f.binary_op(&TokenType::Iff, "P", "Q"), r"(P \equiv Q)");
639 }
640
641 #[test]
642 fn unicode_quantifiers() {
643 let f = UnicodeFormatter;
644 assert_eq!(f.quantifier(&QuantifierKind::Universal, "x", "P(x)"), "∀x(P(x))");
645 assert_eq!(f.quantifier(&QuantifierKind::Existential, "x", "P(x)"), "∃x(P(x))");
646 assert_eq!(f.quantifier(&QuantifierKind::Cardinal(3), "x", "P(x)"), "∃=3.x(P(x))");
647 }
648
649 #[test]
650 fn latex_quantifiers() {
651 let f = LatexFormatter;
652 assert_eq!(f.quantifier(&QuantifierKind::Universal, "x", "P(x)"), "\\forall x(P(x))");
653 assert_eq!(f.quantifier(&QuantifierKind::Existential, "x", "P(x)"), "\\exists x(P(x))");
654 }
655
656 #[test]
657 fn latex_sanitization() {
658 let f = LatexFormatter;
659 assert_eq!(f.sanitize("foo_bar"), r"foo\_bar");
660 assert_eq!(f.sanitize("x^2"), r"x\^{}2");
661 assert_eq!(f.sanitize("a&b"), r"a\&b");
662 }
663
664 #[test]
665 fn unicode_no_sanitization() {
666 let f = UnicodeFormatter;
667 assert_eq!(f.sanitize("foo_bar"), "foo_bar");
668 }
669
670 #[test]
671 fn unicode_lambda() {
672 let f = UnicodeFormatter;
673 assert_eq!(f.lambda("x", "P(x)"), "λx.P(x)");
674 }
675
676 #[test]
677 fn latex_lambda() {
678 let f = LatexFormatter;
679 assert_eq!(f.lambda("x", "P(x)"), "\\lambda x.P(x)");
680 }
681
682 #[test]
683 fn unicode_counterfactual() {
684 let f = UnicodeFormatter;
685 assert_eq!(f.counterfactual("P", "Q"), "(P □→ Q)");
686 }
687
688 #[test]
689 fn latex_counterfactual() {
690 let f = LatexFormatter;
691 assert_eq!(f.counterfactual("P", "Q"), r"(P \boxright Q)");
692 }
693
694 #[test]
696 fn rust_binary_operators() {
697 let f = RustFormatter;
698 assert_eq!(f.binary_op(&TokenType::And, "P", "Q"), "(P && Q)");
699 assert_eq!(f.binary_op(&TokenType::Or, "P", "Q"), "(P || Q)");
700 assert_eq!(f.binary_op(&TokenType::Iff, "P", "Q"), "(P == Q)");
701 }
702
703 #[test]
704 fn rust_implication_desugaring() {
705 let f = RustFormatter;
706 assert_eq!(f.binary_op(&TokenType::If, "P", "Q"), "(!(P) || (Q))");
708 }
709
710 #[test]
711 fn rust_lambda() {
712 let f = RustFormatter;
713 assert_eq!(f.lambda("x", "x > 0"), "|x| { x > 0 }");
714 }
715
716 #[test]
717 fn rust_quantifiers_as_comments() {
718 let f = RustFormatter;
719 assert_eq!(f.universal(), "/* ∀ */");
720 assert_eq!(f.existential(), "/* ∃ */");
721 }
722}