Coverage Report

Created: 2025-09-08 21: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
0
    pub fn new(input: &'a str) -> Self {
53
0
        Self {
54
0
            tokens: TokenStream::new(input),
55
0
            error_recovery: ErrorRecovery::new(),
56
0
            errors: Vec::new(),
57
0
            arena: Arena::new(),
58
0
            interner: StringInterner::new(),
59
0
        }
60
0
    }
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
0
pub(crate) fn parse_expr_recursive(state: &mut ParserState) -> Result<Expr> {
82
0
    parse_expr_with_precedence_recursive(state, 0)
83
0
}
84
85
0
pub(crate) fn parse_expr_with_precedence_recursive(
86
0
    state: &mut ParserState,
87
0
    min_prec: i32,
88
0
) -> Result<Expr> {
89
0
    let mut left = expressions::parse_prefix(state)?;
90
91
    loop {
92
        // Handle postfix operators
93
0
        left = handle_postfix_operators(state, left)?;
94
95
        // Try to handle infix operators
96
0
        if let Some(new_left) = try_handle_infix_operators(state, left.clone(), min_prec)? {
97
0
            left = new_left;
98
0
        } else {
99
0
            break;
100
        }
101
    }
102
103
0
    Ok(left)
104
0
}
105
106
/// Try to handle any infix operator (complexity: 7)
107
0
fn try_handle_infix_operators(
108
0
    state: &mut ParserState,
109
0
    left: Expr,
110
0
    min_prec: i32,
111
0
) -> Result<Option<Expr>> {
112
    // Get current token for infix processing
113
0
    let Some((token, _)) = state.tokens.peek() else {
114
0
        return Ok(None);
115
    };
116
0
    let token_clone = token.clone();
117
118
    // Try operators in order of priority
119
0
    let handlers = [
120
0
        try_new_actor_operators,
121
0
        try_type_cast_operator,
122
0
        try_binary_operators,
123
0
        try_assignment_operators,
124
0
        try_pipeline_operators,
125
0
        try_range_operators,
126
0
    ];
127
    
128
0
    for handler in &handlers {
129
0
        if let Some(new_left) = handler(state, left.clone(), &token_clone, min_prec)? {
130
0
            return Ok(Some(new_left));
131
0
        }
132
    }
133
    
134
0
    Ok(None)
135
0
}
136
137
/// Handle all postfix operators in a loop
138
0
fn handle_postfix_operators(state: &mut ParserState, mut left: Expr) -> Result<Expr> {
139
0
    let mut handled_postfix = true;
140
0
    while handled_postfix {
141
0
        handled_postfix = false;
142
0
        match state.tokens.peek() {
143
            Some((Token::Dot, _)) => {
144
0
                state.tokens.advance();
145
0
                left = functions::parse_method_call(state, left)?;
146
0
                handled_postfix = true;
147
            }
148
            Some((Token::SafeNav, _)) => {
149
0
                state.tokens.advance();
150
0
                left = functions::parse_optional_method_call(state, left)?;
151
0
                handled_postfix = true;
152
            }
153
            Some((Token::LeftParen, _)) => {
154
0
                left = functions::parse_call(state, left)?;
155
0
                handled_postfix = true;
156
            }
157
            Some((Token::LeftBracket, _)) => {
158
0
                left = handle_array_indexing(state, left)?;
159
0
                handled_postfix = true;
160
            }
161
            Some((Token::LeftBrace, _)) => {
162
0
                if let Some(new_left) = try_parse_struct_literal(state, &left)? {
163
0
                    left = new_left;
164
0
                    handled_postfix = true;
165
0
                }
166
            }
167
0
            Some((Token::Increment, _)) => {
168
0
                state.tokens.advance();
169
0
                left = create_post_increment(left);
170
0
                handled_postfix = true;
171
0
            }
172
0
            Some((Token::Decrement, _)) => {
173
0
                state.tokens.advance();
174
0
                left = create_post_decrement(left);
175
0
                handled_postfix = true;
176
0
            }
177
0
            Some((Token::Question, _)) => {
178
0
                state.tokens.advance();
179
0
                left = Expr::new(
180
0
                    ExprKind::Try {
181
0
                        expr: Box::new(left),
182
0
                    },
183
0
                    Span { start: 0, end: 0 },
184
0
                );
185
0
                handled_postfix = true;
186
0
            }
187
0
            _ => {}
188
        }
189
    }
190
0
    Ok(left)
191
0
}
192
193
/// Handle array indexing and slicing syntax `[expr]` or `[start:end]`
194
0
fn handle_array_indexing(state: &mut ParserState, left: Expr) -> Result<Expr> {
195
0
    state.tokens.advance(); // consume [
196
    
197
    // Check for empty slice [:end] 
198
0
    if is_colon_next(state) {
199
0
        return parse_empty_start_slice(state, left);
200
0
    }
201
    
202
0
    let first_expr = parse_expr_recursive(state)?;
203
    
204
    // Check if this is a slice [start:end] or just indexing [index]
205
0
    if is_colon_next(state) {
206
0
        parse_slice_with_start(state, left, first_expr)
207
    } else {
208
0
        parse_index_access(state, left, first_expr)
209
    }
210
0
}
211
212
/// Check if next token is colon (complexity: 1)
213
0
fn is_colon_next(state: &mut ParserState) -> bool {
214
0
    matches!(state.tokens.peek(), Some((Token::Colon, _)))
215
0
}
216
217
/// Parse slice with empty start `[:end]` (complexity: 4)
218
0
fn parse_empty_start_slice(state: &mut ParserState, left: Expr) -> Result<Expr> {
219
0
    state.tokens.advance(); // consume :
220
0
    let end = parse_optional_slice_end(state)?;
221
0
    state.tokens.expect(&Token::RightBracket)?;
222
    
223
0
    Ok(create_slice_expr(left, None, end))
224
0
}
225
226
/// Parse slice with start `[start:end]` (complexity: 3)
227
0
fn parse_slice_with_start(state: &mut ParserState, left: Expr, start: Expr) -> Result<Expr> {
228
0
    state.tokens.advance(); // consume :
229
0
    let end = parse_optional_slice_end(state)?;
230
0
    state.tokens.expect(&Token::RightBracket)?;
231
    
232
0
    Ok(create_slice_expr(left, Some(Box::new(start)), end))
233
0
}
234
235
/// Parse optional slice end expression (complexity: 3)
236
0
fn parse_optional_slice_end(state: &mut ParserState) -> Result<Option<Box<Expr>>> {
237
0
    if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
238
0
        Ok(None)
239
    } else {
240
0
        Ok(Some(Box::new(parse_expr_recursive(state)?)))
241
    }
242
0
}
243
244
/// Parse index access `[index]` (complexity: 2)
245
0
fn parse_index_access(state: &mut ParserState, left: Expr, index: Expr) -> Result<Expr> {
246
0
    state.tokens.expect(&Token::RightBracket)?;
247
    
248
0
    Ok(Expr {
249
0
        kind: ExprKind::IndexAccess {
250
0
            object: Box::new(left),
251
0
            index: Box::new(index),
252
0
        },
253
0
        span: Span { start: 0, end: 0 },
254
0
        attributes: Vec::new(),
255
0
    })
256
0
}
257
258
/// Create slice expression (complexity: 1)
259
0
fn create_slice_expr(object: Expr, start: Option<Box<Expr>>, end: Option<Box<Expr>>) -> Expr {
260
0
    Expr {
261
0
        kind: ExprKind::Slice {
262
0
            object: Box::new(object),
263
0
            start,
264
0
            end,
265
0
        },
266
0
        span: Span { start: 0, end: 0 },
267
0
        attributes: Vec::new(),
268
0
    }
269
0
}
270
271
272
/// Try to parse struct literal
273
0
fn try_parse_struct_literal(state: &mut ParserState, left: &Expr) -> Result<Option<Expr>> {
274
0
    if let ExprKind::Identifier(name) = &left.kind {
275
0
        if name.chars().next().is_some_and(char::is_uppercase) {
276
0
            let name = name.clone();
277
0
            let span = left.span;
278
0
            return Ok(Some(types::parse_struct_literal(state, name, span)?));
279
0
        }
280
0
    }
281
0
    Ok(None)
282
0
}
283
284
/// Create post-increment expression
285
0
fn create_post_increment(left: Expr) -> Expr {
286
0
    Expr {
287
0
        kind: ExprKind::PostIncrement {
288
0
            target: Box::new(left),
289
0
        },
290
0
        span: Span { start: 0, end: 0 },
291
0
        attributes: Vec::new(),
292
0
    }
293
0
}
294
295
/// Create post-decrement expression
296
0
fn create_post_decrement(left: Expr) -> Expr {
297
0
    Expr {
298
0
        kind: ExprKind::PostDecrement {
299
0
            target: Box::new(left),
300
0
        },
301
0
        span: Span { start: 0, end: 0 },
302
0
        attributes: Vec::new(),
303
0
    }
304
0
}
305
306
307
/// Try to parse binary operators
308
0
fn try_binary_operators(
309
0
    state: &mut ParserState,
310
0
    left: Expr,
311
0
    token: &Token,
312
0
    min_prec: i32,
313
0
) -> Result<Option<Expr>> {
314
0
    if let Some(bin_op) = expressions::token_to_binary_op(token) {
315
0
        let prec = expressions::get_precedence(bin_op);
316
0
        if prec < min_prec {
317
0
            return Ok(None);
318
0
        }
319
320
0
        state.tokens.advance();
321
0
        let right = parse_expr_with_precedence_recursive(state, prec + 1)?;
322
0
        Ok(Some(Expr {
323
0
            kind: ExprKind::Binary {
324
0
                left: Box::new(left),
325
0
                op: bin_op,
326
0
                right: Box::new(right),
327
0
            },
328
0
            span: Span { start: 0, end: 0 },
329
0
            attributes: Vec::new(),
330
0
        }))
331
    } else {
332
0
        Ok(None)
333
    }
334
0
}
335
336
/// Try to parse type cast operator (as) - complexity: 5
337
0
fn try_type_cast_operator(
338
0
    state: &mut ParserState,
339
0
    left: Expr,
340
0
    token: &Token,
341
0
    _min_prec: i32,
342
0
) -> Result<Option<Expr>> {
343
0
    if !matches!(token, Token::As) {
344
0
        return Ok(None);
345
0
    }
346
    
347
0
    state.tokens.advance(); // consume 'as'
348
    
349
    // Get the target type
350
0
    let target_type = match state.tokens.peek() {
351
0
        Some((Token::Identifier(t), _)) => {
352
0
            let type_name = t.clone();
353
0
            state.tokens.advance();
354
0
            type_name
355
        }
356
0
        _ => bail!("Expected type name after 'as'"),
357
    };
358
    
359
0
    Ok(Some(Expr {
360
0
        kind: ExprKind::TypeCast {
361
0
            expr: Box::new(left),
362
0
            target_type,
363
0
        },
364
0
        span: Span { start: 0, end: 0 },
365
0
        attributes: Vec::new(),
366
0
    }))
367
0
}
368
369
/// Try to parse new actor operations (<- and <?)
370
0
fn try_new_actor_operators(
371
0
    state: &mut ParserState,
372
0
    left: Expr,
373
0
    token: &Token,
374
0
    min_prec: i32,
375
0
) -> Result<Option<Expr>> {
376
0
    let (expr_kind, _prec) = match token {
377
        Token::LeftArrow => {
378
            // Parse actor <- message
379
0
            let prec = 1; // Same as assignment
380
0
            if prec < min_prec {
381
0
                return Ok(None);
382
0
            }
383
0
            state.tokens.advance();
384
0
            let message = parse_expr_with_precedence_recursive(state, prec)?;
385
0
            (ExprKind::ActorSend {
386
0
                actor: Box::new(left),
387
0
                message: Box::new(message),
388
0
            }, prec)
389
        }
390
        Token::ActorQuery => {
391
            // Parse actor <? message
392
0
            let prec = 1; // Same as assignment
393
0
            if prec < min_prec {
394
0
                return Ok(None);
395
0
            }
396
0
            state.tokens.advance();
397
0
            let message = parse_expr_with_precedence_recursive(state, prec)?;
398
0
            (ExprKind::ActorQuery {
399
0
                actor: Box::new(left),
400
0
                message: Box::new(message),
401
0
            }, prec)
402
        }
403
0
        _ => return Ok(None),
404
    };
405
406
0
    Ok(Some(Expr {
407
0
        kind: expr_kind,
408
0
        span: Span { start: 0, end: 0 },
409
0
        attributes: Vec::new(),
410
0
    }))
411
0
}
412
413
/// Try to parse assignment operators
414
0
fn try_assignment_operators(
415
0
    state: &mut ParserState,
416
0
    left: Expr,
417
0
    token: &Token,
418
0
    min_prec: i32,
419
0
) -> Result<Option<Expr>> {
420
0
    if !token.is_assignment_op() {
421
0
        return Ok(None);
422
0
    }
423
424
0
    let prec = 1;
425
0
    if prec < min_prec {
426
0
        return Ok(None);
427
0
    }
428
429
0
    state.tokens.advance();
430
0
    let value = parse_expr_with_precedence_recursive(state, prec)?;
431
432
0
    let expr = if *token == Token::Equal {
433
0
        Expr {
434
0
            kind: ExprKind::Assign {
435
0
                target: Box::new(left),
436
0
                value: Box::new(value),
437
0
            },
438
0
            span: Span { start: 0, end: 0 },
439
0
            attributes: Vec::new(),
440
0
        }
441
    } else {
442
0
        let bin_op = get_compound_assignment_op(token);
443
0
        Expr {
444
0
            kind: ExprKind::CompoundAssign {
445
0
                target: Box::new(left),
446
0
                op: bin_op,
447
0
                value: Box::new(value),
448
0
            },
449
0
            span: Span { start: 0, end: 0 },
450
0
            attributes: Vec::new(),
451
0
        }
452
    };
453
0
    Ok(Some(expr))
454
0
}
455
456
/// Get binary operator for compound assignment
457
0
fn get_compound_assignment_op(token: &Token) -> BinaryOp {
458
0
    match token {
459
0
        Token::PlusEqual => BinaryOp::Add,
460
0
        Token::MinusEqual => BinaryOp::Subtract,
461
0
        Token::StarEqual => BinaryOp::Multiply,
462
0
        Token::SlashEqual => BinaryOp::Divide,
463
0
        Token::PercentEqual => BinaryOp::Modulo,
464
0
        Token::PowerEqual => BinaryOp::Power,
465
0
        Token::AmpersandEqual => BinaryOp::BitwiseAnd,
466
0
        Token::PipeEqual => BinaryOp::BitwiseOr,
467
0
        Token::CaretEqual => BinaryOp::BitwiseXor,
468
0
        Token::LeftShiftEqual => BinaryOp::LeftShift,
469
0
        _ => unreachable!("Already checked is_assignment_op"),
470
    }
471
0
}
472
473
/// Try to parse pipeline operators (>>)
474
0
fn try_pipeline_operators(
475
0
    state: &mut ParserState,
476
0
    left: Expr,
477
0
    token: &Token,
478
0
    min_prec: i32,
479
0
) -> Result<Option<Expr>> {
480
0
    if !matches!(token, Token::Pipeline) {
481
0
        return Ok(None);
482
0
    }
483
484
0
    let prec = 3;
485
0
    if prec < min_prec {
486
0
        return Ok(None);
487
0
    }
488
489
0
    state.tokens.advance();
490
0
    let stage_expr = parse_expr_with_precedence_recursive(state, prec + 1)?;
491
492
0
    let expr = if let ExprKind::Pipeline { expr, mut stages } = left.kind {
493
0
        stages.push(PipelineStage {
494
0
            op: Box::new(stage_expr),
495
0
            span: Span { start: 0, end: 0 },
496
0
        });
497
0
        Expr {
498
0
            kind: ExprKind::Pipeline { expr, stages },
499
0
            span: Span { start: 0, end: 0 },
500
0
            attributes: Vec::new(),
501
0
        }
502
    } else {
503
0
        Expr {
504
0
            kind: ExprKind::Pipeline {
505
0
                expr: Box::new(left),
506
0
                stages: vec![PipelineStage {
507
0
                    op: Box::new(stage_expr),
508
0
                    span: Span { start: 0, end: 0 },
509
0
                }],
510
0
            },
511
0
            span: Span { start: 0, end: 0 },
512
0
            attributes: Vec::new(),
513
0
        }
514
    };
515
0
    Ok(Some(expr))
516
0
}
517
518
/// Try to parse range operators (.., ..=)
519
0
fn try_range_operators(
520
0
    state: &mut ParserState,
521
0
    left: Expr,
522
0
    token: &Token,
523
0
    min_prec: i32,
524
0
) -> Result<Option<Expr>> {
525
0
    if !matches!(token, Token::DotDot | Token::DotDotEqual) {
526
0
        return Ok(None);
527
0
    }
528
529
0
    let prec = 5;
530
0
    if prec < min_prec {
531
0
        return Ok(None);
532
0
    }
533
534
0
    let inclusive = matches!(token, Token::DotDotEqual);
535
0
    state.tokens.advance();
536
0
    let end = parse_expr_with_precedence_recursive(state, prec + 1)?;
537
0
    Ok(Some(Expr {
538
0
        kind: ExprKind::Range {
539
0
            start: Box::new(left),
540
0
            end: Box::new(end),
541
0
            inclusive,
542
0
        },
543
0
        span: Span { start: 0, end: 0 },
544
0
        attributes: Vec::new(),
545
0
    }))
546
0
}