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}