Skip to main content

miden_assembly_syntax/parser/
value.rs

1use core::fmt;
2
3use miden_core::{
4    Felt,
5    field::PrimeField64,
6    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8
9// PUSH VALUE
10// ================================================================================================
11
12#[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// WORD VALUE
73// ================================================================================================
74
75#[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// INT VALUE
173// ================================================================================================
174
175/// Represents one of the various types of values that have a hex-encoded representation in Miden
176/// Assembly source files.
177#[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    /// A tiny value
184    U8(u8),
185    /// A small value
186    U16(u16),
187    /// A u32 constant, typically represents a memory address
188    U32(u32),
189    /// A single field element, 8 bytes, encoded as 16 hex digits
190    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    /// Returns the value as a `u64`.
228    ///
229    /// This is an alias for [`as_int`](Self::as_int) that matches the `Felt` API.
230    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}