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}