cairo_lang_casm/
ap_change.rs1use 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
41pub trait ApplyApChange: Sized {
43 fn apply_known_ap_change(self, ap_change: usize) -> Option<Self>;
45 fn can_apply_unknown(&self) -> bool;
47
48 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 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}