argumentation/aspic/
language.rs1use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
10pub enum Literal {
11 Atom(String),
13 Neg(String),
15}
16
17impl Literal {
18 pub fn atom(name: impl Into<String>) -> Self {
27 let name = name.into();
28 debug_assert!(
29 !name.starts_with("__applicable_"),
30 "`__applicable_` prefix is reserved for ASPIC+ undercut encoding; use StructuredSystem::add_undercut_rule"
31 );
32 Literal::Atom(name)
33 }
34
35 pub fn neg(name: impl Into<String>) -> Self {
42 let name = name.into();
43 debug_assert!(
44 !name.starts_with("__applicable_"),
45 "`__applicable_` prefix is reserved for ASPIC+ undercut encoding; use StructuredSystem::add_undercut_rule"
46 );
47 Literal::Neg(name)
48 }
49
50 pub(crate) fn undercut_marker(rule_id: usize) -> Literal {
58 Literal::Neg(format!("__applicable_{}", rule_id))
59 }
60
61 pub fn contrary(&self) -> Literal {
63 match self {
64 Literal::Atom(n) => Literal::Neg(n.clone()),
65 Literal::Neg(n) => Literal::Atom(n.clone()),
66 }
67 }
68
69 pub fn is_contrary_of(&self, other: &Literal) -> bool {
71 &self.contrary() == other
72 }
73}
74
75impl fmt::Display for Literal {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 Literal::Atom(n) => write!(f, "{}", n),
79 Literal::Neg(n) => write!(f, "¬{}", n),
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn contrary_of_atom_is_neg() {
90 let p = Literal::atom("p");
91 assert_eq!(p.contrary(), Literal::neg("p"));
92 }
93
94 #[test]
95 fn contrary_is_symmetric() {
96 let p = Literal::atom("p");
97 let np = Literal::neg("p");
98 assert!(p.is_contrary_of(&np));
99 assert!(np.is_contrary_of(&p));
100 }
101
102 #[test]
103 fn different_atoms_are_not_contraries() {
104 let p = Literal::atom("p");
105 let q = Literal::atom("q");
106 assert!(!p.is_contrary_of(&q));
107 }
108
109 #[test]
110 #[should_panic(expected = "__applicable_")]
111 fn atom_rejects_reserved_prefix_in_debug() {
112 let _ = Literal::atom("__applicable_42");
113 }
114
115 #[test]
116 #[should_panic(expected = "__applicable_")]
117 fn neg_rejects_reserved_prefix_in_debug() {
118 let _ = Literal::neg("__applicable_42");
119 }
120
121 #[test]
122 fn undercut_marker_bypasses_the_check() {
123 let m = Literal::undercut_marker(42);
125 assert_eq!(m, Literal::Neg("__applicable_42".to_string()));
126 }
127}