cairo_lang_casm/
ap_change.rs

1use core::fmt::Display;
2
3use crate::operand::{BinOpOperand, CellRef, DerefOrImmediate, Register, ResOperand};
4
5#[cfg(test)]
6#[path = "ap_change_test.rs"]
7mod test;
8
9#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
10pub enum ApChange {
11    Known(usize),
12    Unknown,
13}
14impl Display for ApChange {
15    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16        match self {
17            ApChange::Known(change) => write!(f, "ApChange::Known({change})"),
18            ApChange::Unknown => write!(f, "ApChange::Unknown"),
19        }
20    }
21}
22
23#[derive(Debug, Eq, PartialEq)]
24pub enum ApChangeError {
25    UnknownApChange,
26    OffsetOverflow,
27}
28
29impl Display for ApChangeError {
30    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31        match self {
32            ApChangeError::UnknownApChange => write!(f, "Unknown ap change"),
33            ApChangeError::OffsetOverflow => write!(f, "Offset overflow"),
34        }
35    }
36}
37
38#[cfg(feature = "std")]
39impl std::error::Error for ApChangeError {}
40
41/// Trait for applying ap changes.
42pub trait ApplyApChange: Sized {
43    /// Attempts to apply ap change, fail on overflow only.
44    fn apply_known_ap_change(self, ap_change: usize) -> Option<Self>;
45    /// Can unknown ap change be applied.
46    fn can_apply_unknown(&self) -> bool;
47
48    /// Attempts to apply ap change.
49    fn apply_ap_change(self, ap_change: ApChange) -> Result<Self, ApChangeError> {
50        match ap_change {
51            ApChange::Unknown if self.can_apply_unknown() => Ok(self),
52            ApChange::Unknown => Err(ApChangeError::UnknownApChange),
53            ApChange::Known(ap_change) => {
54                self.apply_known_ap_change(ap_change).ok_or(ApChangeError::OffsetOverflow)
55            }
56        }
57    }
58
59    /// Same as [Self::apply_known_ap_change] but unchecked.
60    fn unchecked_apply_known_ap_change(self, ap_change: usize) -> Self {
61        self.apply_known_ap_change(ap_change).unwrap()
62    }
63}
64
65impl ApplyApChange for ResOperand {
66    fn apply_known_ap_change(self, ap_change: usize) -> Option<Self> {
67        Some(match self {
68            ResOperand::Deref(operand) => {
69                ResOperand::Deref(operand.apply_known_ap_change(ap_change)?)
70            }
71            ResOperand::DoubleDeref(operand, offset) => {
72                ResOperand::DoubleDeref(operand.apply_known_ap_change(ap_change)?, offset)
73            }
74            ResOperand::Immediate(value) => ResOperand::Immediate(value),
75            ResOperand::BinOp(operand) => {
76                ResOperand::BinOp(operand.apply_known_ap_change(ap_change)?)
77            }
78        })
79    }
80
81    fn can_apply_unknown(&self) -> bool {
82        match self {
83            ResOperand::Deref(operand) => operand.can_apply_unknown(),
84            ResOperand::DoubleDeref(operand, ..) => operand.can_apply_unknown(),
85            ResOperand::Immediate(_) => true,
86            ResOperand::BinOp(operand) => operand.can_apply_unknown(),
87        }
88    }
89}
90
91impl ApplyApChange for CellRef {
92    fn apply_known_ap_change(self, ap_change: usize) -> Option<Self> {
93        Some(match &self.register {
94            Register::AP => CellRef {
95                register: Register::AP,
96                offset: self.offset.checked_sub(ap_change.try_into().ok()?)?,
97            },
98            Register::FP => self,
99        })
100    }
101
102    fn can_apply_unknown(&self) -> bool {
103        match &self.register {
104            Register::AP => false,
105            Register::FP => true,
106        }
107    }
108}
109
110impl ApplyApChange for DerefOrImmediate {
111    fn apply_known_ap_change(self, ap_change: usize) -> Option<Self> {
112        Some(match self {
113            DerefOrImmediate::Deref(operand) => {
114                DerefOrImmediate::Deref(operand.apply_known_ap_change(ap_change)?)
115            }
116            DerefOrImmediate::Immediate(operand) => DerefOrImmediate::Immediate(operand),
117        })
118    }
119
120    fn can_apply_unknown(&self) -> bool {
121        match self {
122            DerefOrImmediate::Deref(operand) => operand.can_apply_unknown(),
123            DerefOrImmediate::Immediate(_) => true,
124        }
125    }
126}
127
128impl ApplyApChange for BinOpOperand {
129    fn apply_known_ap_change(self, ap_change: usize) -> Option<Self> {
130        Some(BinOpOperand {
131            op: self.op,
132            a: self.a.apply_known_ap_change(ap_change)?,
133            b: self.b.apply_known_ap_change(ap_change)?,
134        })
135    }
136
137    fn can_apply_unknown(&self) -> bool {
138        self.a.can_apply_unknown() && self.b.can_apply_unknown()
139    }
140}