solar_ast/ast/
yul.rs

1//! Yul AST.
2
3use crate::{AstPath, Box, BoxSlice, DocComments, Lit, StrLit};
4use solar_interface::{Ident, Span};
5
6/// A block of Yul statements: `{ ... }`.
7///
8/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulBlock>
9#[derive(Debug)]
10pub struct Block<'ast> {
11    /// The span of the block, including the `{` and `}`.
12    pub span: Span,
13    /// The statements in the block.
14    pub stmts: BoxSlice<'ast, Stmt<'ast>>,
15}
16
17impl<'ast> std::ops::Deref for Block<'ast> {
18    type Target = [Stmt<'ast>];
19
20    fn deref(&self) -> &Self::Target {
21        self.stmts
22    }
23}
24
25impl<'ast> std::ops::DerefMut for Block<'ast> {
26    fn deref_mut(&mut self) -> &mut Self::Target {
27        self.stmts
28    }
29}
30
31/// A Yul object.
32///
33/// Reference: <https://docs.soliditylang.org/en/latest/yul.html#specification-of-yul-object>
34#[derive(Debug)]
35pub struct Object<'ast> {
36    /// The doc-comments of the object.
37    pub docs: DocComments<'ast>,
38    /// The span of the object, including the `object` keyword, but excluding the doc-comments.
39    pub span: Span,
40    /// The name of the object.
41    pub name: StrLit,
42    /// The `code` block.
43    pub code: CodeBlock<'ast>,
44    /// Sub-objects, if any.
45    pub children: BoxSlice<'ast, Object<'ast>>,
46    /// `data` segments, if any.
47    pub data: BoxSlice<'ast, Data<'ast>>,
48}
49
50/// A Yul `code` block. See [`Object`].
51#[derive(Debug)]
52pub struct CodeBlock<'ast> {
53    /// The span of the code block, including the `code` keyword.
54    ///
55    /// The `code` keyword may not be present in the source code if the object is parsed as a
56    /// plain [`Block`].
57    pub span: Span,
58    /// The `code` block.
59    pub code: Block<'ast>,
60}
61
62/// A Yul `data` segment. See [`Object`].
63#[derive(Debug)]
64pub struct Data<'ast> {
65    /// The span of the code block, including the `data` keyword.
66    pub span: Span,
67    /// The name of the data segment.
68    pub name: StrLit,
69    /// The data. Can only be a `Str` or `HexStr` literal.
70    pub data: Lit<'ast>,
71}
72
73/// A Yul statement.
74///
75/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulStatement>
76#[derive(Debug)]
77pub struct Stmt<'ast> {
78    /// The doc-comments of the statement.
79    pub docs: DocComments<'ast>,
80    /// The span of the statement.
81    pub span: Span,
82    /// The kind of statement.
83    pub kind: StmtKind<'ast>,
84}
85
86/// A kind of Yul statement.
87#[derive(Debug)]
88pub enum StmtKind<'ast> {
89    /// A blocked scope: `{ ... }`.
90    ///
91    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulBlock>
92    Block(Block<'ast>),
93
94    /// A single-variable assignment statement: `x := 1`.
95    ///
96    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulAssignment>
97    AssignSingle(AstPath<'ast>, Expr<'ast>),
98
99    /// A multiple-variable assignment statement: `x, y, z := foo(1, 2)`.
100    ///
101    /// Multi-assignments require a function call on the right-hand side.
102    ///
103    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulAssignment>
104    AssignMulti(BoxSlice<'ast, AstPath<'ast>>, Expr<'ast>),
105
106    /// An expression statement. This can only be a function call.
107    Expr(Expr<'ast>),
108
109    /// An if statement: `if lt(a, b) { ... }`.
110    ///
111    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulIfStatement>
112    If(Expr<'ast>, Block<'ast>),
113
114    /// A for statement: `for {let i := 0} lt(i,10) {i := add(i,1)} { ... }`.
115    ///
116    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulForStatement>
117    ///
118    /// Breakdown of parts: <https://docs.soliditylang.org/en/latest/yul.html#loops>
119    For(Box<'ast, StmtFor<'ast>>),
120
121    /// A switch statement: `switch expr case 0 { ... } default { ... }`.
122    ///
123    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulSwitchStatement>
124    Switch(StmtSwitch<'ast>),
125
126    /// A leave statement: `leave`.
127    Leave,
128
129    /// A break statement: `break`.
130    Break,
131
132    /// A continue statement: `continue`.
133    Continue,
134
135    /// A function definition statement: `function f() { ... }`.
136    FunctionDef(Function<'ast>),
137
138    /// A variable declaration statement: `let x := 0`.
139    ///
140    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulVariableDeclaration>
141    VarDecl(BoxSlice<'ast, Ident>, Option<Expr<'ast>>),
142}
143
144/// A Yul for statement: `for {let i := 0} lt(i,10) {i := add(i,1)} { ... }`.
145///
146/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulForStatement>
147///
148/// Breakdown of parts: <https://docs.soliditylang.org/en/latest/yul.html#loops>
149#[derive(Debug)]
150pub struct StmtFor<'ast> {
151    pub init: Block<'ast>,
152    pub cond: Expr<'ast>,
153    pub step: Block<'ast>,
154    pub body: Block<'ast>,
155}
156
157/// A Yul switch statement can consist of only a default-case or one
158/// or more non-default cases optionally followed by a default-case.
159///
160/// Example switch statement in Yul:
161///
162/// ```solidity
163/// switch exponent
164/// case 0 { result := 1 }
165/// case 1 { result := base }
166/// default { revert(0, 0) }
167/// ```
168///
169/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulSwitchStatement>
170#[derive(Debug)]
171pub struct StmtSwitch<'ast> {
172    pub selector: Expr<'ast>,
173    /// The cases of the switch statement. Includes the default case in the last position, if any.
174    pub cases: BoxSlice<'ast, StmtSwitchCase<'ast>>,
175}
176
177impl<'ast> StmtSwitch<'ast> {
178    /// Returns the default case of the switch statement, if any.
179    pub fn default_case(&self) -> Option<&StmtSwitchCase<'ast>> {
180        self.cases.last().filter(|case| case.constant.is_none())
181    }
182}
183
184/// Represents a non-default case of a Yul switch statement.
185///
186/// See [`StmtSwitch`] for more information.
187#[derive(Debug)]
188pub struct StmtSwitchCase<'ast> {
189    pub span: Span,
190    /// The constant of the case, if any. `None` for the default case.
191    pub constant: Option<Lit<'ast>>,
192    pub body: Block<'ast>,
193}
194
195/// Yul function definition: `function f() -> a, b { ... }`.
196///
197/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulFunctionDefinition>
198#[derive(Debug)]
199pub struct Function<'ast> {
200    pub name: Ident,
201    pub parameters: BoxSlice<'ast, Ident>,
202    pub returns: BoxSlice<'ast, Ident>,
203    pub body: Block<'ast>,
204}
205
206/// A Yul expression.
207#[derive(Debug)]
208pub struct Expr<'ast> {
209    /// The span of the expression.
210    pub span: Span,
211    /// The kind of expression.
212    pub kind: ExprKind<'ast>,
213}
214
215/// A kind of Yul expression.
216#[derive(Debug)]
217pub enum ExprKind<'ast> {
218    /// A single path.
219    Path(AstPath<'ast>),
220    /// A function call: `foo(a, b)`.
221    Call(ExprCall<'ast>),
222    /// A literal.
223    Lit(Box<'ast, Lit<'ast>>),
224}
225
226/// A Yul function call expression: `foo(a, b)`.
227///
228/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulFunctionCall>
229#[derive(Debug)]
230pub struct ExprCall<'ast> {
231    pub name: Ident,
232    pub arguments: BoxSlice<'ast, Expr<'ast>>,
233}