1use alloc::{boxed::Box, string::String, sync::Arc};
2use core::fmt;
3
4use miden_core::FieldElement;
5use miden_debug_types::{SourceSpan, Span, Spanned};
6
7use super::DocString;
8use crate::{
9 Felt,
10 ast::Ident,
11 parser::{IntValue, ParsingError, WordValue},
12};
13
14#[derive(Clone)]
19pub struct Constant {
20 pub span: SourceSpan,
22 pub docs: Option<DocString>,
24 pub name: Ident,
26 pub value: ConstantExpr,
28}
29
30impl Constant {
31 pub fn new(span: SourceSpan, name: Ident, value: ConstantExpr) -> Self {
33 Self { span, docs: None, name, value }
34 }
35
36 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
38 self.docs = docs.map(DocString::new);
39 self
40 }
41}
42
43impl fmt::Debug for Constant {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 f.debug_struct("Constant")
46 .field("docs", &self.docs)
47 .field("name", &self.name)
48 .field("value", &self.value)
49 .finish()
50 }
51}
52
53impl crate::prettier::PrettyPrint for Constant {
54 fn render(&self) -> crate::prettier::Document {
55 use crate::prettier::*;
56
57 let mut doc = self
58 .docs
59 .as_ref()
60 .map(|docstring| docstring.render())
61 .unwrap_or(Document::Empty);
62
63 doc += flatten(const_text("const") + const_text(" ") + display(&self.name));
64 doc += const_text(" = ");
65
66 doc + self.value.render()
67 }
68}
69
70impl Eq for Constant {}
71
72impl PartialEq for Constant {
73 fn eq(&self, other: &Self) -> bool {
74 self.name == other.name && self.value == other.value
75 }
76}
77
78impl Spanned for Constant {
79 fn span(&self) -> SourceSpan {
80 self.span
81 }
82}
83
84#[derive(Clone)]
89pub enum ConstantExpr {
90 Int(Span<IntValue>),
92 Var(Ident),
94 BinaryOp {
96 span: SourceSpan,
97 op: ConstantOp,
98 lhs: Box<ConstantExpr>,
99 rhs: Box<ConstantExpr>,
100 },
101 String(Ident),
103 Word(Span<WordValue>),
105 Hash(HashKind, Ident),
108}
109
110impl ConstantExpr {
111 #[track_caller]
116 pub fn expect_int(&self) -> IntValue {
117 match self {
118 Self::Int(spanned) => spanned.into_inner(),
119 other => panic!("expected constant expression to be a literal, got {other:#?}"),
120 }
121 }
122
123 #[track_caller]
128 pub fn expect_felt(&self) -> Felt {
129 match self {
130 Self::Int(spanned) => Felt::new(spanned.inner().as_int()),
131 other => panic!("expected constant expression to be a literal, got {other:#?}"),
132 }
133 }
134
135 pub fn expect_string(&self) -> Arc<str> {
136 match self {
137 Self::String(spanned) => spanned.clone().into_inner(),
138 other => panic!("expected constant expression to be a string, got {other:#?}"),
139 }
140 }
141
142 pub fn try_fold(self) -> Result<Self, ParsingError> {
149 match self {
150 Self::String(_) | Self::Word(_) | Self::Int(_) | Self::Var(_) | Self::Hash(..) => {
151 Ok(self)
152 },
153 Self::BinaryOp { span, op, lhs, rhs } => {
154 if rhs.is_literal() {
155 let rhs = Self::into_inner(rhs).try_fold()?;
156 match rhs {
157 Self::String(ident) => {
158 Err(ParsingError::StringInArithmeticExpression { span: ident.span() })
159 },
160 Self::Int(rhs) => {
161 let lhs = Self::into_inner(lhs).try_fold()?;
162 match lhs {
163 Self::String(ident) => {
164 Err(ParsingError::StringInArithmeticExpression {
165 span: ident.span(),
166 })
167 },
168 Self::Int(lhs) => {
169 let lhs = lhs.into_inner();
170 let rhs = rhs.into_inner();
171 let is_division =
172 matches!(op, ConstantOp::Div | ConstantOp::IntDiv);
173 let is_division_by_zero = is_division && rhs == Felt::ZERO;
174 if is_division_by_zero {
175 return Err(ParsingError::DivisionByZero { span });
176 }
177 match op {
178 ConstantOp::Add => {
179 Ok(Self::Int(Span::new(span, lhs + rhs)))
180 },
181 ConstantOp::Sub => {
182 Ok(Self::Int(Span::new(span, lhs - rhs)))
183 },
184 ConstantOp::Mul => {
185 Ok(Self::Int(Span::new(span, lhs * rhs)))
186 },
187 ConstantOp::Div => {
188 Ok(Self::Int(Span::new(span, lhs / rhs)))
189 },
190 ConstantOp::IntDiv => {
191 Ok(Self::Int(Span::new(span, lhs / rhs)))
192 },
193 }
194 },
195 lhs => Ok(Self::BinaryOp {
196 span,
197 op,
198 lhs: Box::new(lhs),
199 rhs: Box::new(Self::Int(rhs)),
200 }),
201 }
202 },
203 rhs => {
204 let lhs = Self::into_inner(lhs).try_fold()?;
205 Ok(Self::BinaryOp {
206 span,
207 op,
208 lhs: Box::new(lhs),
209 rhs: Box::new(rhs),
210 })
211 },
212 }
213 } else {
214 let lhs = Self::into_inner(lhs).try_fold()?;
215 Ok(Self::BinaryOp { span, op, lhs: Box::new(lhs), rhs })
216 }
217 },
218 }
219 }
220
221 fn is_literal(&self) -> bool {
222 match self {
223 Self::Int(_) | Self::String(_) | Self::Word(_) | Self::Hash(..) => true,
224 Self::Var(_) => false,
225 Self::BinaryOp { lhs, rhs, .. } => lhs.is_literal() && rhs.is_literal(),
226 }
227 }
228
229 #[inline(always)]
230 #[allow(clippy::boxed_local)]
231 fn into_inner(self: Box<Self>) -> Self {
232 *self
233 }
234}
235
236impl Eq for ConstantExpr {}
237
238impl PartialEq for ConstantExpr {
239 fn eq(&self, other: &Self) -> bool {
240 match (self, other) {
241 (Self::Int(l), Self::Int(y)) => l == y,
242 (Self::Word(l), Self::Word(y)) => l == y,
243 (Self::Var(l), Self::Var(y)) => l == y,
244 (Self::Hash(l_hk, l_i), Self::Hash(r_hk, r_i)) => l_i == r_i && l_hk == r_hk,
245 (
246 Self::BinaryOp { op: lop, lhs: llhs, rhs: lrhs, .. },
247 Self::BinaryOp { op: rop, lhs: rlhs, rhs: rrhs, .. },
248 ) => lop == rop && llhs == rlhs && lrhs == rrhs,
249 _ => false,
250 }
251 }
252}
253
254impl core::hash::Hash for ConstantExpr {
255 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
256 core::mem::discriminant(self).hash(state);
257 match self {
258 Self::Int(value) => value.hash(state),
259 Self::Word(value) => value.hash(state),
260 Self::String(value) => value.hash(state),
261 Self::Var(value) => value.hash(state),
262 Self::Hash(hash_kind, string) => {
263 hash_kind.hash(state);
264 string.hash(state);
265 },
266 Self::BinaryOp { op, lhs, rhs, .. } => {
267 op.hash(state);
268 lhs.hash(state);
269 rhs.hash(state);
270 },
271 }
272 }
273}
274
275impl fmt::Debug for ConstantExpr {
276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 match self {
278 Self::Int(lit) => fmt::Debug::fmt(&**lit, f),
279 Self::Word(lit) => fmt::Debug::fmt(&**lit, f),
280 Self::Var(name) | Self::String(name) => fmt::Debug::fmt(&**name, f),
281 Self::Hash(hash_kind, str) => fmt::Debug::fmt(&(str, hash_kind), f),
282 Self::BinaryOp { op, lhs, rhs, .. } => {
283 f.debug_tuple(op.name()).field(lhs).field(rhs).finish()
284 },
285 }
286 }
287}
288
289impl crate::prettier::PrettyPrint for ConstantExpr {
290 fn render(&self) -> crate::prettier::Document {
291 use crate::prettier::*;
292
293 match self {
294 Self::Int(literal) => display(literal),
295 Self::Word(literal) => display(literal),
296 Self::Var(ident) | Self::String(ident) => display(ident),
297 Self::Hash(hash_kind, str) => {
298 flatten(display(hash_kind) + const_text("(") + display(str) + const_text(")"))
299 },
300 Self::BinaryOp { op, lhs, rhs, .. } => {
301 let single_line = lhs.render() + display(op) + rhs.render();
302 let multi_line = lhs.render() + nl() + (display(op)) + rhs.render();
303 single_line | multi_line
304 },
305 }
306 }
307}
308
309impl Spanned for ConstantExpr {
310 fn span(&self) -> SourceSpan {
311 match self {
312 Self::Int(spanned) => spanned.span(),
313 Self::Word(spanned) => spanned.span(),
314 Self::Hash(_, spanned) => spanned.span(),
315 Self::Var(spanned) | Self::String(spanned) => spanned.span(),
316 Self::BinaryOp { span, .. } => *span,
317 }
318 }
319}
320
321#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
326pub enum ConstantOp {
327 Add,
328 Sub,
329 Mul,
330 Div,
331 IntDiv,
332}
333
334impl ConstantOp {
335 const fn name(&self) -> &'static str {
336 match self {
337 Self::Add => "Add",
338 Self::Sub => "Sub",
339 Self::Mul => "Mul",
340 Self::Div => "Div",
341 Self::IntDiv => "IntDiv",
342 }
343 }
344}
345
346impl fmt::Display for ConstantOp {
347 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348 match self {
349 Self::Add => f.write_str("+"),
350 Self::Sub => f.write_str("-"),
351 Self::Mul => f.write_str("*"),
352 Self::Div => f.write_str("/"),
353 Self::IntDiv => f.write_str("//"),
354 }
355 }
356}
357
358#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
363pub enum HashKind {
364 Word,
366 Event,
368}
369
370impl fmt::Display for HashKind {
371 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372 match self {
373 Self::Word => f.write_str("word"),
374 Self::Event => f.write_str("event"),
375 }
376 }
377}