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