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