Skip to main content

argumentation/aspic/
rules.rs

1//! Strict and defeasible inference rules.
2
3use super::language::Literal;
4
5/// A rule: premises → conclusion, either strict or defeasible.
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct Rule {
8    /// Unique rule id within a rule set.
9    pub id: RuleId,
10    /// Premises (antecedents).
11    pub premises: Vec<Literal>,
12    /// Conclusion (consequent).
13    pub conclusion: Literal,
14    /// Whether the rule is strict (indefeasible) or defeasible.
15    pub kind: RuleKind,
16}
17
18/// A rule id, unique within a `RuleSet`.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub struct RuleId(pub usize);
21
22impl std::fmt::Display for RuleId {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        write!(f, "r{}", self.0)
25    }
26}
27
28/// Whether a rule is strict or defeasible.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum RuleKind {
31    /// Strict rules are indefeasible.
32    Strict,
33    /// Defeasible rules can be undercut.
34    Defeasible,
35}
36
37impl Rule {
38    /// Construct a strict rule.
39    pub fn strict(id: RuleId, premises: Vec<Literal>, conclusion: Literal) -> Self {
40        Self {
41            id,
42            premises,
43            conclusion,
44            kind: RuleKind::Strict,
45        }
46    }
47
48    /// Construct a defeasible rule.
49    pub fn defeasible(id: RuleId, premises: Vec<Literal>, conclusion: Literal) -> Self {
50        Self {
51            id,
52            premises,
53            conclusion,
54            kind: RuleKind::Defeasible,
55        }
56    }
57
58    /// Whether this rule is defeasible.
59    pub fn is_defeasible(&self) -> bool {
60        matches!(self.kind, RuleKind::Defeasible)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn strict_rule_is_not_defeasible() {
70        let r = Rule::strict(RuleId(0), vec![Literal::atom("p")], Literal::atom("q"));
71        assert!(!r.is_defeasible());
72    }
73
74    #[test]
75    fn defeasible_rule_is_defeasible() {
76        let r = Rule::defeasible(RuleId(1), vec![Literal::atom("p")], Literal::atom("q"));
77        assert!(r.is_defeasible());
78    }
79
80    #[test]
81    fn rule_id_displays_as_r_prefix() {
82        assert_eq!(format!("{}", RuleId(0)), "r0");
83        assert_eq!(format!("{}", RuleId(17)), "r17");
84    }
85}