/home/noah/src/ruchy/src/frontend/parser/mod.rs
Line | Count | Source |
1 | | //! Modular parser implementation for Ruchy |
2 | | #![allow(clippy::wildcard_imports)] |
3 | | #![allow(clippy::expect_used)] |
4 | | //! |
5 | | //! The parser is split into logical modules to improve maintainability: |
6 | | //! - `core` - Main parser entry points and precedence handling |
7 | | //! - `expressions` - Basic expressions (literals, binary/unary ops) |
8 | | //! - `control_flow` - Control flow constructs (if, match, loops) |
9 | | //! - `functions` - Function definitions, lambdas, and calls |
10 | | //! - `types` - Type-related parsing (struct, trait, impl) |
11 | | //! - `collections` - Collections (lists, dataframes, comprehensions) |
12 | | //! - `actors` - Actor system constructs |
13 | | //! - `utils` - Parsing utilities and error recovery |
14 | | |
15 | | mod actors; |
16 | | mod collections; |
17 | | mod core; |
18 | | mod expressions; |
19 | | mod functions; |
20 | | mod operator_precedence; |
21 | | mod types; |
22 | | mod utils; |
23 | | |
24 | | // Re-export the main parser |
25 | | pub use core::Parser; |
26 | | |
27 | | use crate::frontend::arena::{Arena, StringInterner}; |
28 | | use crate::frontend::ast::{ |
29 | | ActorHandler, Attribute, BinaryOp, DataFrameColumn, EnumVariant, Expr, ExprKind, ImportItem, Literal, MatchArm, Param, |
30 | | Pattern, PipelineStage, Span, StringPart, StructField, TraitMethod, Type, TypeKind, UnaryOp, |
31 | | }; |
32 | | use crate::frontend::lexer::{Token, TokenStream}; |
33 | | use crate::parser::error_recovery::{ErrorNode, ErrorRecovery}; |
34 | | use anyhow::{bail, Result}; |
35 | | |
36 | | /// Shared parser state and utilities |
37 | | pub(crate) struct ParserState<'a> { |
38 | | pub tokens: TokenStream<'a>, |
39 | | #[allow(dead_code)] |
40 | | pub error_recovery: ErrorRecovery, |
41 | | pub errors: Vec<ErrorNode>, |
42 | | /// Arena allocator for AST nodes |
43 | | #[allow(dead_code)] |
44 | | pub arena: Arena, |
45 | | /// String interner for deduplicating identifiers |
46 | | #[allow(dead_code)] |
47 | | pub interner: StringInterner, |
48 | | } |
49 | | |
50 | | impl<'a> ParserState<'a> { |
51 | | #[must_use] |
52 | 444 | pub fn new(input: &'a str) -> Self { |
53 | 444 | Self { |
54 | 444 | tokens: TokenStream::new(input), |
55 | 444 | error_recovery: ErrorRecovery::new(), |
56 | 444 | errors: Vec::new(), |
57 | 444 | arena: Arena::new(), |
58 | 444 | interner: StringInterner::new(), |
59 | 444 | } |
60 | 444 | } |
61 | | |
62 | | /// Get all errors encountered during parsing |
63 | 0 | pub fn get_errors(&self) -> &[ErrorNode] { |
64 | 0 | &self.errors |
65 | 0 | } |
66 | | |
67 | | /// Get arena statistics for performance monitoring |
68 | | #[allow(dead_code)] |
69 | 0 | pub fn arena_stats(&self) -> (usize, usize) { |
70 | 0 | (self.arena.total_allocated(), self.arena.num_items()) |
71 | 0 | } |
72 | | |
73 | | /// Get interner statistics |
74 | | #[allow(dead_code)] |
75 | 0 | pub fn interner_stats(&self) -> (usize, usize) { |
76 | 0 | self.interner.stats() |
77 | 0 | } |
78 | | } |
79 | | |
80 | | /// Forward declarations for recursive parsing |
81 | 1.10k | pub(crate) fn parse_expr_recursive(state: &mut ParserState) -> Result<Expr> { |
82 | 1.10k | parse_expr_with_precedence_recursive(state, 0) |
83 | 1.10k | } |
84 | | |
85 | | #[allow(clippy::too_many_lines)] |
86 | | #[allow(clippy::cognitive_complexity)] |
87 | 1.35k | pub(crate) fn parse_expr_with_precedence_recursive( |
88 | 1.35k | state: &mut ParserState, |
89 | 1.35k | min_prec: i32, |
90 | 1.35k | ) -> Result<Expr> { |
91 | 1.35k | let mut left1.23k = expressions::parse_prefix(state)?118 ; |
92 | | |
93 | | loop { |
94 | | // Handle postfix operators |
95 | 1.38k | left1.38k = handle_postfix_operators(state, left)?2 ; |
96 | | |
97 | | // Get current token for infix processing |
98 | 1.38k | let Some((token793 , _)) = state.tokens.peek() else { |
99 | 588 | break; |
100 | | }; |
101 | 793 | let token_clone = token.clone(); |
102 | | |
103 | | // Try different infix operator types |
104 | 793 | if let Some(new_left2 ) = try_new_actor_operators(state, left.clone(), &token_clone, min_prec)?0 { |
105 | 2 | left = new_left; |
106 | 2 | continue; |
107 | 791 | } |
108 | | |
109 | 791 | if let Some(new_left143 ) = try_binary_operators(state, left.clone(), &token_clone, min_prec)?2 { |
110 | 143 | left = new_left; |
111 | 143 | continue; |
112 | 646 | } |
113 | | |
114 | 0 | if let Some(new_left) = |
115 | 646 | try_assignment_operators(state, left.clone(), &token_clone, min_prec)?0 |
116 | | { |
117 | 0 | left = new_left; |
118 | 0 | continue; |
119 | 646 | } |
120 | | |
121 | 646 | if let Some(new_left2 ) = try_pipeline_operators(state, left.clone(), &token_clone, min_prec)?0 |
122 | | { |
123 | 2 | left = new_left; |
124 | 2 | continue; |
125 | 644 | } |
126 | | |
127 | 644 | if let Some(new_left4 ) = try_range_operators(state, left.clone(), &token_clone, min_prec)?0 { |
128 | 4 | left = new_left; |
129 | 4 | continue; |
130 | 640 | } |
131 | | |
132 | 640 | break; |
133 | | } |
134 | | |
135 | 1.22k | Ok(left) |
136 | 1.35k | } |
137 | | |
138 | | /// Handle all postfix operators in a loop |
139 | 1.38k | fn handle_postfix_operators(state: &mut ParserState, mut left: Expr) -> Result<Expr> { |
140 | 1.38k | let mut handled_postfix = true; |
141 | 2.94k | while handled_postfix { |
142 | 1.56k | handled_postfix = false; |
143 | 1.56k | match state.tokens.peek() { |
144 | | Some((Token::Dot, _)) => { |
145 | 53 | state.tokens.advance(); |
146 | 53 | left52 = functions::parse_method_call(state, left)?1 ; |
147 | 52 | handled_postfix = true; |
148 | | } |
149 | | Some((Token::SafeNav, _)) => { |
150 | 0 | state.tokens.advance(); |
151 | 0 | left = functions::parse_optional_method_call(state, left)?; |
152 | 0 | handled_postfix = true; |
153 | | } |
154 | | Some((Token::LeftParen, _)) => { |
155 | 129 | left = functions::parse_call(state, left)?0 ; |
156 | 129 | handled_postfix = true; |
157 | | } |
158 | | Some((Token::LeftBracket, _)) => { |
159 | 4 | left3 = handle_array_indexing(state, left)?1 ; |
160 | 3 | handled_postfix = true; |
161 | | } |
162 | | Some((Token::LeftBrace, _)) => { |
163 | 29 | if let Some(new_left1 ) = try_parse_struct_literal(state, &left)?0 { |
164 | 1 | left = new_left; |
165 | 1 | handled_postfix = true; |
166 | 28 | } |
167 | | } |
168 | 0 | Some((Token::Increment, _)) => { |
169 | 0 | state.tokens.advance(); |
170 | 0 | left = create_post_increment(left); |
171 | 0 | handled_postfix = true; |
172 | 0 | } |
173 | 0 | Some((Token::Decrement, _)) => { |
174 | 0 | state.tokens.advance(); |
175 | 0 | left = create_post_decrement(left); |
176 | 0 | handled_postfix = true; |
177 | 0 | } |
178 | 0 | Some((Token::Question, _)) => { |
179 | 0 | state.tokens.advance(); |
180 | 0 | left = Expr::new( |
181 | 0 | ExprKind::Try { |
182 | 0 | expr: Box::new(left), |
183 | 0 | }, |
184 | 0 | Span { start: 0, end: 0 }, |
185 | 0 | ); |
186 | 0 | handled_postfix = true; |
187 | 0 | } |
188 | 1.35k | _ => {} |
189 | | } |
190 | | } |
191 | 1.38k | Ok(left) |
192 | 1.38k | } |
193 | | |
194 | | /// Handle array indexing and slicing syntax [expr] or [start:end] |
195 | 4 | fn handle_array_indexing(state: &mut ParserState, left: Expr) -> Result<Expr> { |
196 | 4 | state.tokens.advance(); // consume [ |
197 | | |
198 | | // Check for empty slice [:end] |
199 | 4 | if matches!(state.tokens.peek(), Some((Token::Colon, _))) { |
200 | 0 | state.tokens.advance(); // consume : |
201 | 0 | let end = if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) { |
202 | 0 | None |
203 | | } else { |
204 | 0 | Some(Box::new(parse_expr_recursive(state)?)) |
205 | | }; |
206 | 0 | state.tokens.expect(&Token::RightBracket)?; |
207 | 0 | return Ok(Expr { |
208 | 0 | kind: ExprKind::Slice { |
209 | 0 | object: Box::new(left), |
210 | 0 | start: None, |
211 | 0 | end, |
212 | 0 | }, |
213 | 0 | span: Span { start: 0, end: 0 }, |
214 | 0 | attributes: Vec::new(), |
215 | 0 | }); |
216 | 4 | } |
217 | | |
218 | 4 | let first_expr3 = parse_expr_recursive(state)?1 ; |
219 | | |
220 | | // Check if this is a slice [start:end] or just indexing [index] |
221 | 3 | if matches!1 (state.tokens.peek(), Some((Token::Colon, _))) { |
222 | 2 | state.tokens.advance(); // consume : |
223 | 2 | let end = if matches!0 (state.tokens.peek(), Some((Token::RightBracket, _))) { |
224 | 2 | None |
225 | | } else { |
226 | 0 | Some(Box::new(parse_expr_recursive(state)?)) |
227 | | }; |
228 | 2 | state.tokens.expect(&Token::RightBracket)?0 ; |
229 | 2 | Ok(Expr { |
230 | 2 | kind: ExprKind::Slice { |
231 | 2 | object: Box::new(left), |
232 | 2 | start: Some(Box::new(first_expr)), |
233 | 2 | end, |
234 | 2 | }, |
235 | 2 | span: Span { start: 0, end: 0 }, |
236 | 2 | attributes: Vec::new(), |
237 | 2 | }) |
238 | | } else { |
239 | 1 | state.tokens.expect(&Token::RightBracket)?0 ; |
240 | 1 | Ok(Expr { |
241 | 1 | kind: ExprKind::IndexAccess { |
242 | 1 | object: Box::new(left), |
243 | 1 | index: Box::new(first_expr), |
244 | 1 | }, |
245 | 1 | span: Span { start: 0, end: 0 }, |
246 | 1 | attributes: Vec::new(), |
247 | 1 | }) |
248 | | } |
249 | 4 | } |
250 | | |
251 | | |
252 | | /// Try to parse struct literal |
253 | 29 | fn try_parse_struct_literal(state: &mut ParserState, left: &Expr) -> Result<Option<Expr>> { |
254 | 29 | if let ExprKind::Identifier(name7 ) = &left.kind { |
255 | 7 | if name.chars().next().is_some_and(char::is_uppercase) { |
256 | 1 | let name = name.clone(); |
257 | 1 | let span = left.span; |
258 | 1 | return Ok(Some(types::parse_struct_literal(state, name, span)?0 )); |
259 | 6 | } |
260 | 22 | } |
261 | 28 | Ok(None) |
262 | 29 | } |
263 | | |
264 | | /// Create post-increment expression |
265 | 0 | fn create_post_increment(left: Expr) -> Expr { |
266 | 0 | Expr { |
267 | 0 | kind: ExprKind::PostIncrement { |
268 | 0 | target: Box::new(left), |
269 | 0 | }, |
270 | 0 | span: Span { start: 0, end: 0 }, |
271 | 0 | attributes: Vec::new(), |
272 | 0 | } |
273 | 0 | } |
274 | | |
275 | | /// Create post-decrement expression |
276 | 0 | fn create_post_decrement(left: Expr) -> Expr { |
277 | 0 | Expr { |
278 | 0 | kind: ExprKind::PostDecrement { |
279 | 0 | target: Box::new(left), |
280 | 0 | }, |
281 | 0 | span: Span { start: 0, end: 0 }, |
282 | 0 | attributes: Vec::new(), |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | |
287 | | /// Try to parse binary operators |
288 | 791 | fn try_binary_operators( |
289 | 791 | state: &mut ParserState, |
290 | 791 | left: Expr, |
291 | 791 | token: &Token, |
292 | 791 | min_prec: i32, |
293 | 791 | ) -> Result<Option<Expr>> { |
294 | 791 | if let Some(bin_op158 ) = expressions::token_to_binary_op(token) { |
295 | 158 | let prec = expressions::get_precedence(bin_op); |
296 | 158 | if prec < min_prec { |
297 | 13 | return Ok(None); |
298 | 145 | } |
299 | | |
300 | 145 | state.tokens.advance(); |
301 | 145 | let right143 = parse_expr_with_precedence_recursive(state, prec + 1)?2 ; |
302 | 143 | Ok(Some(Expr { |
303 | 143 | kind: ExprKind::Binary { |
304 | 143 | left: Box::new(left), |
305 | 143 | op: bin_op, |
306 | 143 | right: Box::new(right), |
307 | 143 | }, |
308 | 143 | span: Span { start: 0, end: 0 }, |
309 | 143 | attributes: Vec::new(), |
310 | 143 | })) |
311 | | } else { |
312 | 633 | Ok(None) |
313 | | } |
314 | 791 | } |
315 | | |
316 | | /// Try to parse new actor operations (<- and <?) |
317 | 793 | fn try_new_actor_operators( |
318 | 793 | state: &mut ParserState, |
319 | 793 | left: Expr, |
320 | 793 | token: &Token, |
321 | 793 | min_prec: i32, |
322 | 793 | ) -> Result<Option<Expr>> { |
323 | 793 | let (expr_kind2 , _prec2 ) = match token { |
324 | | Token::LeftArrow => { |
325 | | // Parse actor <- message |
326 | 1 | let prec = 1; // Same as assignment |
327 | 1 | if prec < min_prec { |
328 | 0 | return Ok(None); |
329 | 1 | } |
330 | 1 | state.tokens.advance(); |
331 | 1 | let message = parse_expr_with_precedence_recursive(state, prec)?0 ; |
332 | 1 | (ExprKind::ActorSend { |
333 | 1 | actor: Box::new(left), |
334 | 1 | message: Box::new(message), |
335 | 1 | }, prec) |
336 | | } |
337 | | Token::ActorQuery => { |
338 | | // Parse actor <? message |
339 | 1 | let prec = 1; // Same as assignment |
340 | 1 | if prec < min_prec { |
341 | 0 | return Ok(None); |
342 | 1 | } |
343 | 1 | state.tokens.advance(); |
344 | 1 | let message = parse_expr_with_precedence_recursive(state, prec)?0 ; |
345 | 1 | (ExprKind::ActorQuery { |
346 | 1 | actor: Box::new(left), |
347 | 1 | message: Box::new(message), |
348 | 1 | }, prec) |
349 | | } |
350 | 791 | _ => return Ok(None), |
351 | | }; |
352 | | |
353 | 2 | Ok(Some(Expr { |
354 | 2 | kind: expr_kind, |
355 | 2 | span: Span { start: 0, end: 0 }, |
356 | 2 | attributes: Vec::new(), |
357 | 2 | })) |
358 | 793 | } |
359 | | |
360 | | /// Try to parse assignment operators |
361 | 646 | fn try_assignment_operators( |
362 | 646 | state: &mut ParserState, |
363 | 646 | left: Expr, |
364 | 646 | token: &Token, |
365 | 646 | min_prec: i32, |
366 | 646 | ) -> Result<Option<Expr>> { |
367 | 646 | if !token.is_assignment_op() { |
368 | 646 | return Ok(None); |
369 | 0 | } |
370 | | |
371 | 0 | let prec = 1; |
372 | 0 | if prec < min_prec { |
373 | 0 | return Ok(None); |
374 | 0 | } |
375 | | |
376 | 0 | state.tokens.advance(); |
377 | 0 | let value = parse_expr_with_precedence_recursive(state, prec)?; |
378 | | |
379 | 0 | let expr = if *token == Token::Equal { |
380 | 0 | Expr { |
381 | 0 | kind: ExprKind::Assign { |
382 | 0 | target: Box::new(left), |
383 | 0 | value: Box::new(value), |
384 | 0 | }, |
385 | 0 | span: Span { start: 0, end: 0 }, |
386 | 0 | attributes: Vec::new(), |
387 | 0 | } |
388 | | } else { |
389 | 0 | let bin_op = get_compound_assignment_op(token); |
390 | 0 | Expr { |
391 | 0 | kind: ExprKind::CompoundAssign { |
392 | 0 | target: Box::new(left), |
393 | 0 | op: bin_op, |
394 | 0 | value: Box::new(value), |
395 | 0 | }, |
396 | 0 | span: Span { start: 0, end: 0 }, |
397 | 0 | attributes: Vec::new(), |
398 | 0 | } |
399 | | }; |
400 | 0 | Ok(Some(expr)) |
401 | 646 | } |
402 | | |
403 | | /// Get binary operator for compound assignment |
404 | 0 | fn get_compound_assignment_op(token: &Token) -> BinaryOp { |
405 | 0 | match token { |
406 | 0 | Token::PlusEqual => BinaryOp::Add, |
407 | 0 | Token::MinusEqual => BinaryOp::Subtract, |
408 | 0 | Token::StarEqual => BinaryOp::Multiply, |
409 | 0 | Token::SlashEqual => BinaryOp::Divide, |
410 | 0 | Token::PercentEqual => BinaryOp::Modulo, |
411 | 0 | Token::PowerEqual => BinaryOp::Power, |
412 | 0 | Token::AmpersandEqual => BinaryOp::BitwiseAnd, |
413 | 0 | Token::PipeEqual => BinaryOp::BitwiseOr, |
414 | 0 | Token::CaretEqual => BinaryOp::BitwiseXor, |
415 | 0 | Token::LeftShiftEqual => BinaryOp::LeftShift, |
416 | 0 | _ => unreachable!("Already checked is_assignment_op"), |
417 | | } |
418 | 0 | } |
419 | | |
420 | | /// Try to parse pipeline operators (>>) |
421 | 646 | fn try_pipeline_operators( |
422 | 646 | state: &mut ParserState, |
423 | 646 | left: Expr, |
424 | 646 | token: &Token, |
425 | 646 | min_prec: i32, |
426 | 646 | ) -> Result<Option<Expr>> { |
427 | 646 | if !matches!643 (token, Token::Pipeline) { |
428 | 643 | return Ok(None); |
429 | 3 | } |
430 | | |
431 | 3 | let prec = 3; |
432 | 3 | if prec < min_prec { |
433 | 1 | return Ok(None); |
434 | 2 | } |
435 | | |
436 | 2 | state.tokens.advance(); |
437 | 2 | let stage_expr = parse_expr_with_precedence_recursive(state, prec + 1)?0 ; |
438 | | |
439 | 2 | let expr = if let ExprKind::Pipeline { expr1 , mut stages1 } = left.kind { |
440 | 1 | stages.push(PipelineStage { |
441 | 1 | op: Box::new(stage_expr), |
442 | 1 | span: Span { start: 0, end: 0 }, |
443 | 1 | }); |
444 | 1 | Expr { |
445 | 1 | kind: ExprKind::Pipeline { expr, stages }, |
446 | 1 | span: Span { start: 0, end: 0 }, |
447 | 1 | attributes: Vec::new(), |
448 | 1 | } |
449 | | } else { |
450 | 1 | Expr { |
451 | 1 | kind: ExprKind::Pipeline { |
452 | 1 | expr: Box::new(left), |
453 | 1 | stages: vec![PipelineStage { |
454 | 1 | op: Box::new(stage_expr), |
455 | 1 | span: Span { start: 0, end: 0 }, |
456 | 1 | }], |
457 | 1 | }, |
458 | 1 | span: Span { start: 0, end: 0 }, |
459 | 1 | attributes: Vec::new(), |
460 | 1 | } |
461 | | }; |
462 | 2 | Ok(Some(expr)) |
463 | 646 | } |
464 | | |
465 | | /// Try to parse range operators (.., ..=) |
466 | 644 | fn try_range_operators( |
467 | 644 | state: &mut ParserState, |
468 | 644 | left: Expr, |
469 | 644 | token: &Token, |
470 | 644 | min_prec: i32, |
471 | 644 | ) -> Result<Option<Expr>> { |
472 | 644 | if !matches!640 (token, Token::DotDot | Token::DotDotEqual) { |
473 | 640 | return Ok(None); |
474 | 4 | } |
475 | | |
476 | 4 | let prec = 5; |
477 | 4 | if prec < min_prec { |
478 | 0 | return Ok(None); |
479 | 4 | } |
480 | | |
481 | 4 | let inclusive = matches!3 (token, Token::DotDotEqual); |
482 | 4 | state.tokens.advance(); |
483 | 4 | let end = parse_expr_with_precedence_recursive(state, prec + 1)?0 ; |
484 | 4 | Ok(Some(Expr { |
485 | 4 | kind: ExprKind::Range { |
486 | 4 | start: Box::new(left), |
487 | 4 | end: Box::new(end), |
488 | 4 | inclusive, |
489 | 4 | }, |
490 | 4 | span: Span { start: 0, end: 0 }, |
491 | 4 | attributes: Vec::new(), |
492 | 4 | })) |
493 | 644 | } |