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