cairo_lang_casm/
ap_change.rs

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