Coverage Report

Created: 2025-09-05 15:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}