miden_assembly_syntax/parser/
value.rs1use core::fmt;
2
3use miden_core::{
4 Felt,
5 field::PrimeField64,
6 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PushValue {
14 Int(IntValue),
15 Word(WordValue),
16}
17
18impl From<u8> for PushValue {
19 fn from(value: u8) -> Self {
20 Self::Int(value.into())
21 }
22}
23
24impl From<u16> for PushValue {
25 fn from(value: u16) -> Self {
26 Self::Int(value.into())
27 }
28}
29
30impl From<u32> for PushValue {
31 fn from(value: u32) -> Self {
32 Self::Int(value.into())
33 }
34}
35
36impl From<Felt> for PushValue {
37 fn from(value: Felt) -> Self {
38 Self::Int(value.into())
39 }
40}
41
42impl From<IntValue> for PushValue {
43 fn from(value: IntValue) -> Self {
44 Self::Int(value)
45 }
46}
47
48impl From<WordValue> for PushValue {
49 fn from(value: WordValue) -> Self {
50 Self::Word(value)
51 }
52}
53
54impl fmt::Display for PushValue {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 Self::Int(value) => fmt::Display::fmt(value, f),
58 Self::Word(value) => fmt::Display::fmt(value, f),
59 }
60 }
61}
62
63impl crate::prettier::PrettyPrint for PushValue {
64 fn render(&self) -> crate::prettier::Document {
65 match self {
66 Self::Int(value) => value.render(),
67 Self::Word(value) => value.render(),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76#[cfg_attr(
77 all(feature = "arbitrary", test),
78 miden_test_serialization_macros::serialization_test
79)]
80pub struct WordValue(pub [Felt; 4]);
81
82impl fmt::Display for WordValue {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 let mut builder = f.debug_list();
85 for value in self.0 {
86 builder.entry(&value.as_canonical_u64());
87 }
88 builder.finish()
89 }
90}
91
92impl crate::prettier::PrettyPrint for WordValue {
93 fn render(&self) -> crate::prettier::Document {
94 use crate::prettier::*;
95
96 const_text("[")
97 + self
98 .0
99 .iter()
100 .copied()
101 .map(display)
102 .reduce(|acc, doc| acc + const_text(",") + doc)
103 .unwrap_or_default()
104 + const_text("]")
105 }
106}
107
108impl PartialOrd for WordValue {
109 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
110 Some(self.cmp(other))
111 }
112}
113
114impl Ord for WordValue {
115 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
116 let (WordValue([l0, l1, l2, l3]), WordValue([r0, r1, r2, r3])) = (self, other);
117 l0.as_canonical_u64()
118 .cmp(&r0.as_canonical_u64())
119 .then_with(|| l1.as_canonical_u64().cmp(&r1.as_canonical_u64()))
120 .then_with(|| l2.as_canonical_u64().cmp(&r2.as_canonical_u64()))
121 .then_with(|| l3.as_canonical_u64().cmp(&r3.as_canonical_u64()))
122 }
123}
124
125impl core::hash::Hash for WordValue {
126 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
127 let WordValue([a, b, c, d]) = self;
128 [
129 a.as_canonical_u64(),
130 b.as_canonical_u64(),
131 c.as_canonical_u64(),
132 d.as_canonical_u64(),
133 ]
134 .hash(state)
135 }
136}
137
138#[cfg(feature = "arbitrary")]
139impl proptest::arbitrary::Arbitrary for WordValue {
140 type Parameters = ();
141
142 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
143 use proptest::{array::uniform4, strategy::Strategy};
144 uniform4((0..crate::FIELD_MODULUS).prop_map(Felt::new_unchecked))
145 .prop_map(WordValue)
146 .no_shrink()
147 .boxed()
148 }
149
150 type Strategy = proptest::prelude::BoxedStrategy<Self>;
151}
152
153impl Serializable for WordValue {
154 fn write_into<W: ByteWriter>(&self, target: &mut W) {
155 self.0[0].write_into(target);
156 self.0[1].write_into(target);
157 self.0[2].write_into(target);
158 self.0[3].write_into(target);
159 }
160}
161
162impl Deserializable for WordValue {
163 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
164 let a = Felt::read_from(source)?;
165 let b = Felt::read_from(source)?;
166 let c = Felt::read_from(source)?;
167 let d = Felt::read_from(source)?;
168 Ok(Self([a, b, c, d]))
169 }
170}
171
172#[derive(Debug, Copy, Clone, PartialEq, Eq)]
178#[cfg_attr(
179 all(feature = "arbitrary", test),
180 miden_test_serialization_macros::serialization_test
181)]
182pub enum IntValue {
183 U8(u8),
185 U16(u16),
187 U32(u32),
189 Felt(Felt),
191}
192
193impl From<u8> for IntValue {
194 fn from(value: u8) -> Self {
195 Self::U8(value)
196 }
197}
198
199impl From<u16> for IntValue {
200 fn from(value: u16) -> Self {
201 Self::U16(value)
202 }
203}
204
205impl From<u32> for IntValue {
206 fn from(value: u32) -> Self {
207 Self::U32(value)
208 }
209}
210
211impl From<Felt> for IntValue {
212 fn from(value: Felt) -> Self {
213 Self::Felt(value)
214 }
215}
216
217impl IntValue {
218 pub fn as_int(&self) -> u64 {
219 match self {
220 Self::U8(value) => *value as u64,
221 Self::U16(value) => *value as u64,
222 Self::U32(value) => *value as u64,
223 Self::Felt(value) => value.as_canonical_u64(),
224 }
225 }
226
227 pub fn as_canonical_u64(&self) -> u64 {
231 self.as_int()
232 }
233
234 pub fn checked_add(&self, rhs: Self) -> Option<Self> {
235 let value = self.as_int().checked_add(rhs.as_int())?;
236 if value >= crate::FIELD_MODULUS {
237 return None;
238 }
239 Some(shrink_u64_hex(value))
240 }
241
242 pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
243 let value = self.as_int().checked_sub(rhs.as_int())?;
244 if value >= crate::FIELD_MODULUS {
245 return None;
246 }
247 Some(shrink_u64_hex(value))
248 }
249
250 pub fn checked_mul(&self, rhs: Self) -> Option<Self> {
251 let value = self.as_int().checked_mul(rhs.as_int())?;
252 if value >= crate::FIELD_MODULUS {
253 return None;
254 }
255 Some(shrink_u64_hex(value))
256 }
257
258 pub fn checked_div(&self, rhs: Self) -> Option<Self> {
259 let value = self.as_int().checked_div(rhs.as_int())?;
260 if value >= crate::FIELD_MODULUS {
261 return None;
262 }
263 Some(shrink_u64_hex(value))
264 }
265}
266
267impl core::ops::Add<IntValue> for IntValue {
268 type Output = IntValue;
269
270 fn add(self, rhs: IntValue) -> Self::Output {
271 shrink_u64_hex(self.as_int() + rhs.as_int())
272 }
273}
274
275impl core::ops::Sub<IntValue> for IntValue {
276 type Output = IntValue;
277
278 fn sub(self, rhs: IntValue) -> Self::Output {
279 shrink_u64_hex(self.as_int() - rhs.as_int())
280 }
281}
282
283impl core::ops::Mul<IntValue> for IntValue {
284 type Output = IntValue;
285
286 fn mul(self, rhs: IntValue) -> Self::Output {
287 shrink_u64_hex(self.as_int() * rhs.as_int())
288 }
289}
290
291impl core::ops::Div<IntValue> for IntValue {
292 type Output = IntValue;
293
294 fn div(self, rhs: IntValue) -> Self::Output {
295 shrink_u64_hex(self.as_int() / rhs.as_int())
296 }
297}
298
299impl PartialEq<Felt> for IntValue {
300 fn eq(&self, other: &Felt) -> bool {
301 match self {
302 Self::U8(lhs) => (*lhs as u64) == other.as_canonical_u64(),
303 Self::U16(lhs) => (*lhs as u64) == other.as_canonical_u64(),
304 Self::U32(lhs) => (*lhs as u64) == other.as_canonical_u64(),
305 Self::Felt(lhs) => lhs == other,
306 }
307 }
308}
309
310impl fmt::Display for IntValue {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 match self {
313 Self::U8(value) => write!(f, "{value}"),
314 Self::U16(value) => write!(f, "{value}"),
315 Self::U32(value) => write!(f, "{value:#04x}"),
316 Self::Felt(value) => write!(f, "{:#08x}", value.as_canonical_u64().to_be()),
317 }
318 }
319}
320
321impl crate::prettier::PrettyPrint for IntValue {
322 fn render(&self) -> crate::prettier::Document {
323 match self {
324 Self::U8(v) => v.render(),
325 Self::U16(v) => v.render(),
326 Self::U32(v) => v.render(),
327 Self::Felt(v) => v.as_canonical_u64().render(),
328 }
329 }
330}
331
332impl PartialOrd for IntValue {
333 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
334 Some(self.cmp(other))
335 }
336}
337
338impl Ord for IntValue {
339 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
340 use core::cmp::Ordering;
341 match (self, other) {
342 (Self::U8(l), Self::U8(r)) => l.cmp(r),
343 (Self::U8(_), _) => Ordering::Less,
344 (Self::U16(_), Self::U8(_)) => Ordering::Greater,
345 (Self::U16(l), Self::U16(r)) => l.cmp(r),
346 (Self::U16(_), _) => Ordering::Less,
347 (Self::U32(_), Self::U8(_) | Self::U16(_)) => Ordering::Greater,
348 (Self::U32(l), Self::U32(r)) => l.cmp(r),
349 (Self::U32(_), _) => Ordering::Less,
350 (Self::Felt(_), Self::U8(_) | Self::U16(_) | Self::U32(_)) => Ordering::Greater,
351 (Self::Felt(l), Self::Felt(r)) => l.as_canonical_u64().cmp(&r.as_canonical_u64()),
352 }
353 }
354}
355
356impl core::hash::Hash for IntValue {
357 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
358 core::mem::discriminant(self).hash(state);
359 match self {
360 Self::U8(value) => value.hash(state),
361 Self::U16(value) => value.hash(state),
362 Self::U32(value) => value.hash(state),
363 Self::Felt(value) => value.as_canonical_u64().hash(state),
364 }
365 }
366}
367
368impl Serializable for IntValue {
369 fn write_into<W: ByteWriter>(&self, target: &mut W) {
370 self.as_int().write_into(target)
371 }
372}
373
374impl Deserializable for IntValue {
375 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
376 let raw = source.read_u64()?;
377 if raw >= Felt::ORDER_U64 {
378 Err(DeserializationError::InvalidValue(
379 "int value is greater than field modulus".into(),
380 ))
381 } else {
382 Ok(shrink_u64_hex(raw))
383 }
384 }
385}
386
387#[cfg(feature = "arbitrary")]
388impl proptest::arbitrary::Arbitrary for IntValue {
389 type Parameters = ();
390
391 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
392 use proptest::{num, prop_oneof, strategy::Strategy};
393 prop_oneof![
394 num::u8::ANY.prop_map(IntValue::U8),
395 (u8::MAX as u16 + 1..=u16::MAX).prop_map(IntValue::U16),
396 (u16::MAX as u32 + 1..=u32::MAX).prop_map(IntValue::U32),
397 (num::u64::ANY).prop_filter_map("valid felt value", |n| {
398 if n > u32::MAX as u64 && n < crate::FIELD_MODULUS {
399 Some(IntValue::Felt(Felt::new_unchecked(n)))
400 } else {
401 None
402 }
403 }),
404 ]
405 .no_shrink()
406 .boxed()
407 }
408
409 type Strategy = proptest::prelude::BoxedStrategy<Self>;
410}
411
412#[inline]
413pub(crate) fn shrink_u64_hex(n: u64) -> IntValue {
414 if u8::try_from(n).is_ok() {
415 IntValue::U8(n as u8)
416 } else if u16::try_from(n).is_ok() {
417 IntValue::U16(n as u16)
418 } else if u32::try_from(n).is_ok() {
419 IntValue::U32(n as u32)
420 } else {
421 IntValue::Felt(Felt::new_unchecked(n))
422 }
423}