air_parser/ast/statement.rs
1//! This module provides AST structures for statements which may appear in one of the following:
2//!
3//! * Evaluator function bodies
4//! * The `boundary_constraints` section
5//! * The `integrity_constraints` section
6//!
7//! Statements do not return any value, unlike expressions.
8use std::fmt;
9
10use miden_diagnostics::{SourceSpan, Spanned};
11
12use super::*;
13
14/// Statements are top-level expressions in the body of evaluators,
15/// or in the `boundary_constraints` or `integrity_constraints` sections.
16/// These expressions are called statements because they do not evaluate
17/// to a value, instead they are evaluated sequentially.
18#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
19pub enum Statement {
20 /// Binds an identifier to an expression in the following statements, e.g. `let x = y * 2`
21 ///
22 /// A let statement contains all following statements in its containing block as
23 /// the "body" of the let. In other words, it imposes a new lexical scope within the
24 /// block in which the variable it binds is visible. Because of this, a let statement
25 /// will always be the last statement in a block when one is present.
26 ///
27 /// Furthermore, the parser guarantees that a let statement always has a body, which
28 /// by induction guarantees that a let statement will always have a constraint in its body
29 /// at some point, otherwise parsing would fail. This guarantee holds during all analyses
30 /// and transformations.
31 Let(Let),
32 /// Represents a value expression in the tail position of a block
33 ///
34 /// This is only used in pure function contexts, and during certain transformations. It
35 /// is not valid in any position but the last statement of a block, and that block must
36 /// be in an expression context (i.e. pure function body, let-bound expression that expands
37 /// during inlining to a block of statements that are used to build up a value).
38 Expr(Expr),
39 /// Declares a constraint to be enforced on a single value.
40 ///
41 /// This variant accepts a [ScalarExpr] for simplicity in the parser, but is expected to always
42 /// be either a call to an evaluator function, or a binary expression of the form `lhs = rhs`,
43 /// i.e. an equality. This is validated by the semantic analyzer.
44 Enforce(ScalarExpr),
45 /// Declares a constraint to be conditionally enforced.
46 ///
47 /// This has all the same semantics as `Enforce`, except it has a condition expression which
48 /// determines if the constraint will be enforced.
49 ///
50 /// This variant is only present in the AST after inlining is performed, even though the parser
51 /// could produce it directly from the parse tree. This is because this variant is equivalent to
52 /// a comprehension constraint with a single element, so we transform all syntax corresponding to
53 /// `EnforceIf` into `EnforceAll` form so we can reuse all of the analyses/optimizations/transformations
54 /// for both. However, when lowering to the IR, we perform inlining/unrolling of comprehensions, and
55 /// at that time we need `EnforceIf` in order to represent unrolled constraints which have a selector
56 /// that is only resolvable at runtime.
57 EnforceIf(#[span] ScalarExpr, ScalarExpr),
58 /// Declares a constraint to be enforced over a vector of values produced by a comprehension.
59 ///
60 /// Just like `Enforce`, except the constraint is contained in the body of a list comprehension,
61 /// and must be enforced on every value produced by that comprehension.
62 EnforceAll(ListComprehension),
63 /// Declares a bus related constraint
64 BusEnforce(ListComprehension),
65}
66impl Statement {
67 /// Checks this statement to see if it contains any constraints
68 ///
69 /// This is primarily necessary because `let` statements have a body, which is
70 /// also composed of statements, and so may be nested arbitrarily deep, containing
71 /// one or more constraints in its body.
72 pub fn has_constraints(&self) -> bool {
73 match self {
74 Self::Enforce(_)
75 | Self::EnforceIf(_, _)
76 | Self::EnforceAll(_)
77 | Self::BusEnforce(_) => true,
78 Self::Let(Let { body, .. }) => body.iter().any(|s| s.has_constraints()),
79 Self::Expr(_) => false,
80 }
81 }
82
83 pub fn display(&self, indent: usize) -> DisplayStatement<'_> {
84 DisplayStatement {
85 statement: self,
86 indent,
87 }
88 }
89}
90impl From<Expr> for Statement {
91 fn from(expr: Expr) -> Self {
92 match expr {
93 Expr::Let(let_expr) => Self::Let(*let_expr),
94 expr => Self::Expr(expr),
95 }
96 }
97}
98impl TryFrom<ScalarExpr> for Statement {
99 type Error = ();
100
101 fn try_from(expr: ScalarExpr) -> Result<Self, Self::Error> {
102 match expr {
103 ScalarExpr::Let(let_expr) => Ok(Self::Let(*let_expr)),
104 expr => Expr::try_from(expr).map_err(|_| ()).map(Self::Expr),
105 }
106 }
107}
108
109/// A `let` statement binds `name` to the value of `expr` in `body`.
110#[derive(Clone, Spanned)]
111pub struct Let {
112 #[span]
113 pub span: SourceSpan,
114 /// The identifier to be bound
115 pub name: Identifier,
116 /// The expression to bind
117 pub value: Expr,
118 /// The statements for which this binding will be visible.
119 ///
120 /// For example, given the following:
121 ///
122 /// ```airscript
123 /// integrity_constraints {
124 /// let x = 2
125 /// let y = x^2
126 /// enf clk = x
127 /// enf clk' = clk + y
128 /// }
129 /// ```
130 ///
131 /// When parsed, the syntax tree for the `integrity_constraints` block
132 /// would have a single [Statement], the [Let] corresponding to `let x = 2`.
133 /// The `body` of that let would also contain a single [Statement], another
134 /// [Let] corresponding to `let y = x^2`, which in turn would contain the
135 /// two constraint statements in its `body`.
136 ///
137 /// In other words, when present, a [Let] introduces a new block/lexical scope,
138 /// and all subsequent statements are included in that block. The `body` of a [Let]
139 /// is that block. A [Let] will always be the final statement in its containing block,
140 /// e.g. `integrity_constraints`, but may be preceded by any number of non-[Let] statements.
141 pub body: Vec<Statement>,
142}
143impl Let {
144 pub fn new(span: SourceSpan, name: Identifier, value: Expr, body: Vec<Statement>) -> Self {
145 Self {
146 span,
147 name,
148 value,
149 body,
150 }
151 }
152
153 /// Return the type of the overall `let` expression.
154 ///
155 /// A `let` with an empty body, or with a body that terminates with a non-expression statement
156 /// has no type (or rather, one could consider the type it returns to be of "void" or "unit" type).
157 ///
158 /// For `let` statements with a non-empty body that terminates with an expression, the `let` can
159 /// be used in expression position, producing the value of the terminating expression in its body,
160 /// and having the same type as that value.
161 pub fn ty(&self) -> Option<Type> {
162 let mut last = self.body.last();
163 while let Some(stmt) = last.take() {
164 match stmt {
165 Statement::Let(let_expr) => {
166 last = let_expr.body.last();
167 }
168 Statement::Expr(expr) => return expr.ty(),
169 Statement::Enforce(_)
170 | Statement::EnforceIf(_, _)
171 | Statement::EnforceAll(_)
172 | Statement::BusEnforce(_) => break,
173 }
174 }
175
176 None
177 }
178}
179impl Eq for Let {}
180impl PartialEq for Let {
181 fn eq(&self, other: &Self) -> bool {
182 self.name == other.name && self.value == other.value && self.body == other.body
183 }
184}
185impl fmt::Debug for Let {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 f.debug_struct("Let")
188 .field("name", &self.name)
189 .field("value", &self.value)
190 .field("body", &self.body)
191 .finish()
192 }
193}