1use super::SeqSep;
2use crate::{PResult, Parser};
3use smallvec::SmallVec;
4use solar_ast::{
5 AstPath, Box, DocComments, Lit, LitKind, PathSlice, StrKind, StrLit, Symbol, token::*, yul::*,
6};
7use solar_interface::{Ident, error_code, kw, sym};
8
9impl<'sess, 'ast> Parser<'sess, 'ast> {
10 #[instrument(level = "debug", skip_all)]
15 pub fn parse_yul_file_object(&mut self) -> PResult<'sess, Object<'ast>> {
16 let docs = self.parse_doc_comments();
17 let object = if self.check_keyword(sym::object) {
18 self.parse_yul_object(docs)
19 } else {
20 let lo = self.token.span;
21 self.parse_yul_block().map(|code| {
22 let span = lo.to(self.prev_token.span);
23 let name = StrLit { span, value: sym::object };
24 let code = CodeBlock { span, code };
25 Object { docs, span, name, code, children: Box::default(), data: Box::default() }
26 })
27 }?;
28 self.expect(TokenKind::Eof)?;
29 Ok(object)
30 }
31
32 pub fn parse_yul_object(&mut self, docs: DocComments<'ast>) -> PResult<'sess, Object<'ast>> {
36 let lo = self.token.span;
37 self.expect_keyword(sym::object)?;
38 let name = self.parse_str_lit()?;
39
40 self.expect(TokenKind::OpenDelim(Delimiter::Brace))?;
41 let code = self.parse_yul_code()?;
42 let mut children = Vec::new();
43 let mut data = Vec::new();
44 loop {
45 let docs = self.parse_doc_comments();
46 if self.check_keyword(sym::object) {
47 children.push(self.parse_yul_object(docs)?);
48 } else if self.check_keyword(sym::data) {
49 data.push(self.parse_yul_data()?);
50 } else {
51 break;
52 }
53 }
54 self.expect(TokenKind::CloseDelim(Delimiter::Brace))?;
55
56 let span = lo.to(self.prev_token.span);
57 let children = self.alloc_vec(children);
58 let data = self.alloc_vec(data);
59 Ok(Object { docs, span, name, code, children, data })
60 }
61
62 fn parse_yul_code(&mut self) -> PResult<'sess, CodeBlock<'ast>> {
64 let lo = self.token.span;
65 self.expect_keyword(sym::code)?;
66 let code = self.parse_yul_block()?;
67 let span = lo.to(self.prev_token.span);
68 Ok(CodeBlock { span, code })
69 }
70
71 fn parse_yul_data(&mut self) -> PResult<'sess, Data<'ast>> {
73 let lo = self.token.span;
74 self.expect_keyword(sym::data)?;
75 let name = self.parse_str_lit()?;
76 let data = self.parse_yul_lit()?;
77 if !matches!(data.kind, LitKind::Str(StrKind::Str | StrKind::Hex, ..)) {
78 let msg = "only string and hex string literals are allowed in `data` segments";
79 return Err(self.dcx().err(msg).span(data.span));
80 }
81 let span = lo.to(self.prev_token.span);
82 Ok(Data { span, name, data })
83 }
84
85 fn parse_yul_lit(&mut self) -> PResult<'sess, Lit<'ast>> {
86 let (lit, subdenomination) = self.parse_lit(false)?;
87 assert!(subdenomination.is_none());
88 Ok(lit)
89 }
90
91 pub fn parse_yul_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
93 self.in_yul(Self::parse_yul_stmt_unchecked)
94 }
95
96 pub fn parse_yul_stmt_unchecked(&mut self) -> PResult<'sess, Stmt<'ast>> {
98 self.with_recursion_limit("Yul statement", |this| {
99 let docs = this.parse_doc_comments();
100 this.parse_spanned(Self::parse_yul_stmt_kind).map(|(span, kind)| Stmt {
101 docs,
102 span,
103 kind,
104 })
105 })
106 }
107
108 pub fn parse_yul_block(&mut self) -> PResult<'sess, Block<'ast>> {
110 self.in_yul(Self::parse_yul_block_unchecked)
111 }
112
113 pub fn parse_yul_block_unchecked(&mut self) -> PResult<'sess, Block<'ast>> {
115 let lo = self.token.span;
116 self.parse_delim_seq(Delimiter::Brace, SeqSep::none(), true, Self::parse_yul_stmt_unchecked)
117 .map(|stmts| {
118 let span = lo.to(self.prev_token.span);
119 Block { span, stmts }
120 })
121 }
122
123 fn parse_yul_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
125 if self.eat_keyword(kw::Let) {
126 self.parse_yul_stmt_var_decl()
127 } else if self.eat_keyword(kw::Function) {
128 self.parse_yul_function()
129 } else if self.check(TokenKind::OpenDelim(Delimiter::Brace)) {
130 self.parse_yul_block_unchecked().map(StmtKind::Block)
131 } else if self.eat_keyword(kw::If) {
132 self.parse_yul_stmt_if()
133 } else if self.eat_keyword(kw::Switch) {
134 self.parse_yul_stmt_switch().map(StmtKind::Switch)
135 } else if self.eat_keyword(kw::For) {
136 self.parse_yul_stmt_for()
137 } else if self.eat_keyword(kw::Break) {
138 Ok(StmtKind::Break)
139 } else if self.eat_keyword(kw::Continue) {
140 Ok(StmtKind::Continue)
141 } else if self.eat_keyword(kw::Leave) {
142 Ok(StmtKind::Leave)
143 } else if self.check_ident() {
144 let lo = self.token.span;
145 let path = self.parse_path_any()?;
146 if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
147 let name = self.expect_single_ident_path(path);
148 let (hi, call) = self.parse_spanned(|this| this.parse_yul_expr_call_with(name))?;
149 let span = lo.to(hi);
150 Ok(StmtKind::Expr(Expr { span, kind: ExprKind::Call(call) }))
151 } else if self.eat(TokenKind::Walrus) {
152 self.check_valid_path(&path);
153 let expr = self.parse_yul_expr()?;
154 Ok(StmtKind::AssignSingle(path, expr))
155 } else if self.check(TokenKind::Comma) {
156 self.check_valid_path(&path);
157 let mut paths = SmallVec::<[_; 4]>::new();
158 paths.push(path);
159 while self.eat(TokenKind::Comma) {
160 paths.push(self.parse_yul_path()?);
161 }
162 let paths = self.alloc_smallvec(paths);
163 self.expect(TokenKind::Walrus)?;
164 let expr = self.parse_yul_expr()?;
165 let ExprKind::Call(_expr) = &expr.kind else {
166 let msg = "only function calls are allowed in multi-assignment";
167 return Err(self.dcx().err(msg).span(expr.span));
168 };
169 Ok(StmtKind::AssignMulti(paths, expr))
170 } else {
171 self.unexpected()
172 }
173 } else {
174 self.unexpected()
175 }
176 }
177
178 fn parse_yul_stmt_var_decl(&mut self) -> PResult<'sess, StmtKind<'ast>> {
180 let mut idents = SmallVec::<[_; 8]>::new();
181 loop {
182 idents.push(self.parse_ident()?);
183 if !self.eat(TokenKind::Comma) {
184 break;
185 }
186 }
187 let idents = self.alloc_smallvec(idents);
188 let expr = if self.eat(TokenKind::Walrus) { Some(self.parse_yul_expr()?) } else { None };
189 Ok(StmtKind::VarDecl(idents, expr))
190 }
191
192 fn parse_yul_function(&mut self) -> PResult<'sess, StmtKind<'ast>> {
194 let name = self.parse_ident()?;
195 let parameters = self.parse_paren_comma_seq(true, Self::parse_ident)?;
196 let returns = if self.eat(TokenKind::Arrow) {
197 self.parse_nodelim_comma_seq(
198 TokenKind::OpenDelim(Delimiter::Brace),
199 false,
200 Self::parse_ident,
201 )?
202 } else {
203 Default::default()
204 };
205 let body = self.parse_yul_block_unchecked()?;
206 Ok(StmtKind::FunctionDef(Function { name, parameters, returns, body }))
207 }
208
209 fn parse_yul_stmt_if(&mut self) -> PResult<'sess, StmtKind<'ast>> {
211 let cond = self.parse_yul_expr()?;
212 let body = self.parse_yul_block_unchecked()?;
213 Ok(StmtKind::If(cond, body))
214 }
215
216 fn parse_yul_stmt_switch(&mut self) -> PResult<'sess, StmtSwitch<'ast>> {
218 let lo = self.prev_token.span;
219 let selector = self.parse_yul_expr()?;
220 let mut cases = Vec::new();
221 while self.check_keyword(kw::Case) {
222 cases.push(self.parse_yul_stmt_switch_case(kw::Case)?);
223 }
224 let default_case = if self.check_keyword(kw::Default) {
225 Some(self.parse_yul_stmt_switch_case(kw::Default)?)
226 } else {
227 None
228 };
229 if cases.is_empty() {
230 let span = lo.to(self.prev_token.span);
231 if default_case.is_none() {
232 self.dcx().err("`switch` statement has no cases").span(span).emit();
233 } else {
234 self.dcx()
235 .warn("`switch` statement has only a default case")
236 .span(span)
237 .code(error_code!(9592))
238 .emit();
239 }
240 }
241 if let Some(default_case) = default_case {
242 cases.push(default_case);
243 }
244 let cases = self.alloc_vec(cases);
245 Ok(StmtSwitch { selector, cases })
246 }
247
248 fn parse_yul_stmt_switch_case(&mut self, kw: Symbol) -> PResult<'sess, StmtSwitchCase<'ast>> {
249 self.parse_spanned(|this| {
250 debug_assert!(this.token.is_keyword(kw));
251 this.bump();
252 let constant = if kw == kw::Case {
253 let lit = this.parse_yul_lit()?;
254 this.expect_no_subdenomination();
255 Some(lit)
256 } else {
257 None
258 };
259 let body = this.parse_yul_block_unchecked()?;
260 Ok((constant, body))
261 })
262 .map(|(span, (constant, body))| StmtSwitchCase { span, constant, body })
263 }
264
265 fn parse_yul_stmt_for(&mut self) -> PResult<'sess, StmtKind<'ast>> {
267 let init = self.parse_yul_block_unchecked()?;
268 let cond = self.parse_yul_expr()?;
269 let step = self.parse_yul_block_unchecked()?;
270 let body = self.parse_yul_block_unchecked()?;
271 Ok(StmtKind::For(self.alloc(StmtFor { init, cond, step, body })))
272 }
273
274 fn parse_yul_expr(&mut self) -> PResult<'sess, Expr<'ast>> {
276 self.parse_spanned(Self::parse_yul_expr_kind).map(|(span, kind)| Expr { span, kind })
277 }
278
279 fn parse_yul_expr_kind(&mut self) -> PResult<'sess, ExprKind<'ast>> {
281 if self.check_lit() {
282 self.parse_yul_lit().map(|lit| ExprKind::Lit(self.alloc(lit)))
284 } else if self.check_path() {
285 let path = self.parse_path_any()?;
286 if self.token.is_open_delim(Delimiter::Parenthesis) {
287 let ident = self.expect_single_ident_path(path);
289 self.parse_yul_expr_call_with(ident).map(ExprKind::Call)
290 } else {
291 self.check_valid_path(&path);
292 Ok(ExprKind::Path(path))
293 }
294 } else {
295 self.unexpected()
296 }
297 }
298
299 fn parse_yul_expr_call_with(&mut self, name: Ident) -> PResult<'sess, ExprCall<'ast>> {
301 if !name.is_yul_evm_builtin() && name.is_reserved(true) {
302 self.expected_ident_found_other(name.into(), false).unwrap_err().emit();
303 }
304 let arguments = self.parse_paren_comma_seq(true, Self::parse_yul_expr)?;
305 Ok(ExprCall { name, arguments })
306 }
307
308 #[track_caller]
310 fn expect_single_ident_path(&mut self, path: AstPath<'_>) -> Ident {
311 if path.segments().len() > 1 {
312 self.dcx().err("fully-qualified paths aren't allowed here").span(path.span()).emit();
313 }
314 *path.last()
315 }
316
317 fn parse_yul_path(&mut self) -> PResult<'sess, AstPath<'ast>> {
318 let path = self.parse_path_any()?;
319 self.check_valid_path(&path);
320 Ok(path)
321 }
322
323 #[track_caller]
325 fn check_valid_path(&mut self, path: &PathSlice) {
326 let first = *path.first();
329 if first.is_yul_keyword() || (path.segments().len() == 1 && first.is_yul_evm_builtin()) {
330 self.expected_ident_found_other(first.into(), false).unwrap_err().emit();
331 }
332 for &ident in &path.segments()[1..] {
333 if ident.is_yul_keyword() {
334 self.expected_ident_found_other(ident.into(), false).unwrap_err().emit();
335 }
336 }
337 }
338}