## 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/collections.rs

    Line
    Count
    Source
    1
    //! Collections parsing (lists, dataframes, comprehensions, blocks, object literals)
    2
    3
    use super::{ParserState, *};
    4
    use crate::frontend::ast::{DataFrameColumn, Literal, ObjectField};
    5
    6
    /// Parse a block expression or object literal
    7
    ///
    8
    /// Blocks are sequences of expressions enclosed in braces `{}`. This function
    9
    /// intelligently detects whether the content represents a block of statements
    10
    /// or an object literal based on the syntax patterns.
    11
    ///
    12
    /// # Examples
    13
    ///
    14
    /// ```
    15
    /// use ruchy::Parser;
    16
    ///
    17
    /// let input = "{ let x = 5; x + 1 }";
    18
    /// let mut parser = Parser::new(input);
    19
    /// let result = parser.parse();
    20
    /// assert!(result.is_ok());
    21
    /// ```
    22
    ///
    23
    /// # Errors
    24
    ///
    25
    /// Returns an error if:
    26
    /// - The opening brace is missing (should be handled by caller)
    27
    /// - Failed to parse any expression within the block
    28
    /// - Missing closing brace
    29
    /// - Invalid object literal syntax when detected as object
    30
    /// # Errors
    31
    ///
    32
    /// Returns an error if the operation fails
    33
    /// Parse a block expression { ... } (complexity: 7)
    34
    ///
    35
    /// Handles both regular blocks and let-statement conversion to let-expressions
    36
    0
    pub fn parse_block(state: &mut ParserState) -> Result<Expr> {
    37
    0
        let start_span = state.tokens.advance().expect("checked by parser logic").1; // consume {
    38
    39
        // Check if this might be an object literal
    40
    0
        if is_object_literal(state) {
    41
    0
            return parse_object_literal_body(state, start_span);
    42
    0
        }
    43
    44
    0
        let exprs = parse_block_expressions(state, start_span)?;
    45
    0
        state.tokens.expect(&Token::RightBrace)?;
    46
    47
    0
        Ok(create_block_result(exprs, start_span))
    48
    0
    }
    49
    50
    /// Parse all expressions within a block (complexity: 8)
    51
    0
    fn parse_block_expressions(state: &mut ParserState, start_span: Span) -> Result<Vec<Expr>> {
    52
    0
        let mut exprs = Vec::new();
    53
    54
    0
        while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    55
    0
            let expr = parse_next_block_expression(state, start_span)?;
    56
    0
            exprs.push(expr);
    57
    58
    0
            consume_optional_semicolon(state);
    59
    60
    0
            if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    61
    0
                break;
    62
    0
            }
    63
        }
    64
    65
    0
        Ok(exprs)
    66
    0
    }
    67
    68
    /// Parse the next expression in a block, handling let statements (complexity: 9)
    69
    0
    fn parse_next_block_expression(state: &mut ParserState, start_span: Span) -> Result<Expr> {
    70
    0
        if matches!(state.tokens.peek(), Some((Token::Let, _))) {
    71
    0
            parse_potential_let_statement(state, start_span)
    72
        } else {
    73
    0
            super::parse_expr_recursive(state)
    74
        }
    75
    0
    }
    76
    77
    /// Handle potential let statement with lookahead (complexity: 10)
    78
    0
    fn parse_potential_let_statement(state: &mut ParserState, start_span: Span) -> Result<Expr> {
    79
    0
        let saved_pos = state.tokens.position();
    80
    0
        state.tokens.advance(); // consume let
    81
    82
    0
        if let Some(let_info) = try_parse_let_binding(state)? {
    83
    0
            if is_let_expression(state) {
    84
                // Let expression - restore and parse normally
    85
    0
                state.tokens.set_position(saved_pos);
    86
    0
                super::parse_expr_recursive(state)
    87
            } else {
    88
                // Let statement - convert to let expression
    89
    0
                create_let_statement_expression(state, let_info, start_span)
    90
            }
    91
        } else {
    92
            // Not a valid let - restore and parse as expression
    93
    0
            state.tokens.set_position(saved_pos);
    94
    0
            super::parse_expr_recursive(state)
    95
        }
    96
    0
    }
    97
    98
    /// Try to parse let binding info (complexity: 6)
    99
    0
    fn try_parse_let_binding(state: &mut ParserState) -> Result<Option<LetBindingInfo>> {
    100
    0
        if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
    101
    0
            let name = name.clone();
    102
    0
            state.tokens.advance();
    103
    104
    0
            if matches!(state.tokens.peek(), Some((Token::Equal, _))) {
    105
    0
                state.tokens.advance(); // consume =
    106
    0
                let value = super::parse_expr_recursive(state)?;
    107
    0
                return Ok(Some(LetBindingInfo { name, value }));
    108
    0
            }
    109
    0
        }
    110
    0
        Ok(None)
    111
    0
    }
    112
    113
    /// Check if this is a let expression (has 'in' keyword) (complexity: 2)
    114
    0
    fn is_let_expression(state: &mut ParserState) -> bool {
    115
    0
        matches!(state.tokens.peek(), Some((Token::In, _)))
    116
    0
    }
    117
    118
    /// Create let statement expression from binding info (complexity: 8)
    119
    0
    fn create_let_statement_expression(state: &mut ParserState, let_info: LetBindingInfo, start_span: Span) -> Result<Expr> {
    120
    0
        consume_optional_semicolon(state);
    121
    122
    0
        let body = parse_remaining_block_body(state, start_span)?;
    123
    124
    0
        Ok(Expr::new(
    125
    0
            ExprKind::Let {
    126
    0
                name: let_info.name,
    127
    0
                type_annotation: None,
    128
    0
                value: Box::new(let_info.value),
    129
    0
                body: Box::new(body),
    130
    0
                is_mutable: false,
    131
    0
            },
    132
    0
            start_span,
    133
    0
        ))
    134
    0
    }
    135
    136
    /// Parse remaining expressions as block body (complexity: 8)
    137
    0
    fn parse_remaining_block_body(state: &mut ParserState, start_span: Span) -> Result<Expr> {
    138
    0
        let mut body_exprs = Vec::new();
    139
    140
    0
        while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    141
    0
            body_exprs.push(super::parse_expr_recursive(state)?);
    142
    143
    0
            consume_optional_semicolon(state);
    144
    145
    0
            if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    146
    0
                break;
    147
    0
            }
    148
        }
    149
    150
    0
        Ok(create_body_expression(body_exprs, start_span))
    151
    0
    }
    152
    153
    /// Create body expression from parsed expressions (complexity: 4)
    154
    0
    fn create_body_expression(body_exprs: Vec<Expr>, start_span: Span) -> Expr {
    155
    0
        if body_exprs.is_empty() {
    156
    0
            Expr::new(ExprKind::Literal(Literal::Unit), start_span)
    157
    0
        } else if body_exprs.len() == 1 {
    158
    0
            body_exprs.into_iter().next().expect("checked: len == 1")
    159
        } else {
    160
    0
            Expr::new(ExprKind::Block(body_exprs), start_span)
    161
        }
    162
    0
    }
    163
    164
    /// Create final block result (complexity: 3)
    165
    0
    fn create_block_result(exprs: Vec<Expr>, start_span: Span) -> Expr {
    166
    0
        if exprs.is_empty() {
    167
    0
            Expr::new(ExprKind::Literal(Literal::Unit), start_span)
    168
        } else {
    169
    0
            Expr::new(ExprKind::Block(exprs), start_span)
    170
        }
    171
    0
    }
    172
    173
    /// Consume optional semicolon (complexity: 2)
    174
    0
    fn consume_optional_semicolon(state: &mut ParserState) {
    175
    0
        if matches!(state.tokens.peek(), Some((Token::Semicolon, _))) {
    176
    0
            state.tokens.advance();
    177
    0
        }
    178
    0
    }
    179
    180
    /// Information about a let binding (complexity: 1)
    181
    #[derive(Debug, Clone)]
    182
    struct LetBindingInfo {
    183
        name: String,
    184
        value: Expr,
    185
    }
    186
    187
    /// Check if the current position looks like an object literal
    188
    ///
    189
    /// Analyzes the upcoming tokens to determine if the content should be parsed
    190
    /// as an object literal rather than a regular block. Object literals have
    191
    /// specific patterns like `key: value` pairs or spread operators `...expr`.
    192
    ///
    193
    /// # Examples
    194
    ///
    195
    /// Returns `true` for patterns like:
    196
    /// - `{ name: "John" }`
    197
    /// - `{ ...other }`
    198
    /// - `{ "key": value }`
    199
    ///
    200
    /// Returns `false` for:
    201
    /// - `{ x + 1 }`
    202
    /// - `{ let x = 5 }`
    203
    /// - `{ }`
    204
    ///
    205
    /// # Errors
    206
    ///
    207
    /// Returns an error if token stream operations fail during lookahead.
    208
    0
    fn is_object_literal(state: &mut ParserState) -> bool {
    209
        // Peek at the next few tokens to determine if this is an object literal
    210
        // Object literal patterns:
    211
        // 1. { key: value, ... }
    212
        // 2. { ...spread }
    213
        // 3. { } (empty)
    214
    215
        // Empty braces could be either - default to block
    216
    0
        if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    217
    0
            return false;
    218
    0
        }
    219
    220
        // Check for spread operator
    221
    0
        if matches!(state.tokens.peek(), Some((Token::DotDotDot, _))) {
    222
    0
            return true;
    223
    0
        }
    224
    225
        // Check for identifier/string followed by colon or fat arrow (book compatibility)
    226
    0
        match state.tokens.peek() {
    227
            Some((Token::Identifier(_) | Token::String(_) | Token::RawString(_), _)) => {
    228
                // Look ahead for colon or fat arrow
    229
    0
                let saved_pos = state.tokens.position();
    230
    0
                state.tokens.advance(); // skip identifier/string
    231
    0
                let has_separator = matches!(
    232
    0
                    state.tokens.peek(),
    233
                    Some((Token::Colon | Token::FatArrow, _))
    234
                );
    235
    0
                state.tokens.set_position(saved_pos); // restore position
    236
    0
                has_separator
    237
            }
    238
    0
            _ => false,
    239
        }
    240
    0
    }
    241
    242
    /// Parse an object key, handling identifiers, strings, and reserved words
    243
    /// Complexity: 8 (extracted from `parse_object_literal_body`)
    244
    0
    fn parse_object_key(state: &mut ParserState) -> Result<String> {
    245
    0
        if let Some((token, _)) = state.tokens.peek() {
    246
    0
            let key = token_to_object_key(token)?;
    247
    0
            state.tokens.advance();
    248
    0
            Ok(key)
    249
        } else {
    250
    0
            bail!("Expected key in object literal")
    251
        }
    252
    0
    }
    253
    254
    /// Convert a token to an object key string
    255
    /// Complexity: 7 (simple match statement)
    256
    0
    fn token_to_object_key(token: &Token) -> Result<String> {
    257
    0
        match token {
    258
    0
            Token::Identifier(name) => Ok(name.clone()),
    259
    0
            Token::String(s) | Token::RawString(s) => Ok(s.clone()),
    260
            // Allow reserved words as object keys
    261
    0
            Token::Command => Ok("command".to_string()),
    262
    0
            Token::Type => Ok("type".to_string()),
    263
    0
            Token::Module => Ok("module".to_string()),
    264
    0
            Token::Import => Ok("import".to_string()),
    265
    0
            Token::Export => Ok("export".to_string()),
    266
    0
            Token::Fun => Ok("fun".to_string()),
    267
    0
            Token::Fn => Ok("fn".to_string()),
    268
    0
            Token::Return => Ok("return".to_string()),
    269
    0
            Token::If => Ok("if".to_string()),
    270
    0
            Token::Else => Ok("else".to_string()),
    271
    0
            Token::For => Ok("for".to_string()),
    272
    0
            Token::While => Ok("while".to_string()),
    273
    0
            Token::Loop => Ok("loop".to_string()),
    274
    0
            Token::Match => Ok("match".to_string()),
    275
    0
            Token::Let => Ok("let".to_string()),
    276
    0
            Token::Var => Ok("var".to_string()),
    277
    0
            Token::Const => Ok("const".to_string()),
    278
    0
            Token::Static => Ok("static".to_string()),
    279
    0
            Token::Pub => Ok("pub".to_string()),
    280
    0
            Token::Mut => Ok("mut".to_string()),
    281
    0
            Token::Struct => Ok("struct".to_string()),
    282
    0
            Token::Enum => Ok("enum".to_string()),
    283
    0
            Token::Impl => Ok("impl".to_string()),
    284
    0
            Token::Trait => Ok("trait".to_string()),
    285
    0
            Token::Use => Ok("use".to_string()),
    286
    0
            Token::As => Ok("as".to_string()),
    287
    0
            Token::In => Ok("in".to_string()),
    288
    0
            Token::Where => Ok("where".to_string()),
    289
    0
            Token::Async => Ok("async".to_string()),
    290
    0
            Token::Await => Ok("await".to_string()),
    291
    0
            Token::Try => Ok("try".to_string()),
    292
    0
            Token::Catch => Ok("catch".to_string()),
    293
    0
            Token::Throw => Ok("throw".to_string()),
    294
    0
            Token::Break => Ok("break".to_string()),
    295
    0
            Token::Continue => Ok("continue".to_string()),
    296
    0
            Token::State => Ok("state".to_string()),
    297
    0
            _ => bail!("Expected identifier or string key in object literal"),
    298
        }
    299
    0
    }
    300
    301
    /// Parse the body of an object literal after the opening brace
    302
    ///
    303
    /// Parses the contents of an object literal including key-value pairs and
    304
    /// spread expressions. Handles both string and identifier keys.
    305
    ///
    306
    /// # Examples
    307
    ///
    308
    /// ```
    309
    /// use ruchy::Parser;
    310
    ///
    311
    /// let input = r#"{ name: "John", age: 30 }"#;
    312
    /// let mut parser = Parser::new(input);
    313
    /// let result = parser.parse();
    314
    /// assert!(result.is_ok());
    315
    /// ```
    316
    ///
    317
    /// # Errors
    318
    ///
    319
    /// Returns an error if:
    320
    /// - Invalid key type (neither identifier nor string)
    321
    /// - Missing colon after key
    322
    /// - Failed to parse value expression
    323
    /// - Missing comma between fields
    324
    /// - Missing closing brace
    325
    0
    fn parse_object_literal_body(state: &mut ParserState, start_span: Span) -> Result<Expr> {
    326
    0
        let mut fields = Vec::new();
    327
    328
    0
        while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    329
    0
            parse_single_object_field(state, &mut fields)?;
    330
    0
            handle_object_field_separator(state)?;
    331
        }
    332
    333
    0
        state.tokens.expect(&Token::RightBrace)?;
    334
    0
        Ok(Expr::new(ExprKind::ObjectLiteral { fields }, start_span))
    335
    0
    }
    336
    337
    /// Parse a single object field (either spread or key-value) - complexity: 6
    338
    0
    fn parse_single_object_field(state: &mut ParserState, fields: &mut Vec<ObjectField>) -> Result<()> {
    339
    0
        if matches!(state.tokens.peek(), Some((Token::DotDotDot, _))) {
    340
    0
            parse_object_spread_field(state, fields)
    341
        } else {
    342
    0
            parse_object_key_value_field(state, fields)
    343
        }
    344
    0
    }
    345
    346
    /// Parse object spread field (...expr) - complexity: 3
    347
    0
    fn parse_object_spread_field(state: &mut ParserState, fields: &mut Vec<ObjectField>) -> Result<()> {
    348
    0
        state.tokens.advance(); // consume ...
    349
    0
        let expr = super::parse_expr_recursive(state)?;
    350
    0
        fields.push(ObjectField::Spread { expr });
    351
    0
        Ok(())
    352
    0
    }
    353
    354
    /// Parse object key-value field (key: value or key => value) - complexity: 5
    355
    0
    fn parse_object_key_value_field(state: &mut ParserState, fields: &mut Vec<ObjectField>) -> Result<()> {
    356
    0
        let key = parse_object_key(state)?;
    357
    358
        // Accept either : or => for object key-value pairs (book compatibility)
    359
    0
        if matches!(state.tokens.peek(), Some((Token::FatArrow, _))) {
    360
    0
            state.tokens.advance(); // consume =>
    361
    0
        } else {
    362
    0
            state.tokens.expect(&Token::Colon)?;
    363
        }
    364
    365
    0
        let value = super::parse_expr_recursive(state)?;
    366
    0
        fields.push(ObjectField::KeyValue { key, value });
    367
    0
        Ok(())
    368
    0
    }
    369
    370
    /// Handle comma separator between object fields - complexity: 4
    371
    0
    fn handle_object_field_separator(state: &mut ParserState) -> Result<()> {
    372
    0
        if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
    373
    0
            state.tokens.advance();
    374
    0
            Ok(())
    375
    0
        } else if !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
    376
    0
            bail!("Expected comma or closing brace in object literal")
    377
        } else {
    378
    0
            Ok(())
    379
        }
    380
    0
    }
    381
    382
    /// Parse a list expression or list comprehension
    383
    ///
    384
    /// Parses list literals enclosed in brackets `[]`. Automatically detects
    385
    /// list comprehensions when the `for` keyword is encountered after the
    386
    /// first element.
    387
    ///
    388
    /// # Examples
    389
    ///
    390
    /// ```
    391
    /// use ruchy::Parser;
    392
    ///
    393
    /// let input = "[1, 2, 3]";
    394
    /// let mut parser = Parser::new(input);
    395
    /// let result = parser.parse();
    396
    /// assert!(result.is_ok());
    397
    /// ```
    398
    ///
    399
    /// # Errors
    400
    ///
    401
    /// Returns an error if:
    402
    /// - Failed to parse any element expression
    403
    /// - Missing closing bracket
    404
    /// - Invalid list comprehension syntax
    405
    /// - Malformed comma-separated elements
    406
    /// # Errors
    407
    ///
    408
    /// Returns an error if the operation fails
    409
    0
    pub fn parse_list(state: &mut ParserState) -> Result<Expr> {
    410
    0
        let start_span = state.tokens.advance().expect("checked by parser logic").1; // consume [
    411
    412
        // Check for empty list
    413
    0
        if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
    414
    0
            state.tokens.advance(); // consume ]
    415
    0
            return Ok(Expr::new(ExprKind::List(Vec::new()), start_span));
    416
    0
        }
    417
    418
        // Parse the first element (checking for spread syntax)
    419
    0
        let first_element = parse_list_element(state)?;
    420
    421
        // Check if this is a list comprehension by looking for 'for'
    422
    0
        if matches!(state.tokens.peek(), Some((Token::For, _))) {
    423
    0
            return parse_list_comprehension(state, start_span, first_element);
    424
    0
        }
    425
    426
        // Regular list - continue parsing elements
    427
    0
        let mut elements = vec![first_element];
    428
    429
    0
        while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
    430
    0
            state.tokens.advance(); // consume comma
    431
    432
    0
            if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
    433
    0
                break; // trailing comma
    434
    0
            }
    435
    436
    0
            elements.push(parse_list_element(state)?);
    437
        }
    438
    439
    0
        state.tokens.expect(&Token::RightBracket)?;
    440
    441
    0
        Ok(Expr::new(ExprKind::List(elements), start_span))
    442
    0
    }
    443
    444
    /// Parse a single list element, handling both regular expressions and spread syntax
    445
    0
    fn parse_list_element(state: &mut ParserState) -> Result<Expr> {
    446
        // Check for spread syntax (...)
    447
    0
        if matches!(state.tokens.peek(), Some((Token::DotDotDot, _))) {
    448
    0
            let start_pos = state.tokens.advance().expect("checked above").1.start; // consume ...
    449
    0
            let expr = super::parse_expr_recursive(state)?;
    450
    0
            let span = Span {
    451
    0
                start: start_pos,
    452
    0
                end: expr.span.end
    453
    0
            };
    454
    0
            Ok(Expr::new(ExprKind::Spread { expr: Box::new(expr) }, span))
    455
        } else {
    456
            // Regular element
    457
    0
            super::parse_expr_recursive(state)
    458
        }
    459
    0
    }
    460
    461
    /// Parse a list comprehension after the element expression
    462
    ///
    463
    /// Parses the remaining parts of a list comprehension: the `for` clause,
    464
    /// variable binding, iterable expression, and optional `if` condition.
    465
    ///
    466
    /// # Examples
    467
    ///
    468
    /// ```
    469
    /// use ruchy::Parser;
    470
    ///
    471
    /// let input = "[x * 2 for x in range(10)]";
    472
    /// let mut parser = Parser::new(input);
    473
    /// let result = parser.parse();
    474
    /// assert!(result.is_ok());
    475
    /// ```
    476
    ///
    477
    /// # Errors
    478
    ///
    479
    /// Returns an error if:
    480
    /// - Missing `for` keyword
    481
    /// - Invalid variable name
    482
    /// - Missing `in` keyword
    483
    /// - Failed to parse iterable expression
    484
    /// - Failed to parse condition expression (when present)
    485
    /// - Missing closing bracket
    486
    ///
    487
    /// Parse a condition expression for list comprehension that stops at ]
    488
    0
    fn parse_condition_expr(state: &mut ParserState) -> Result<Expr> {
    489
        // Save the current position in case we need to backtrack
    490
    0
        let _start_pos = state.tokens.position();
    491
    492
        // Try to parse an expression, but we need to be careful about ]
    493
        // We'll parse terms and operators manually to avoid consuming ]
    494
    0
        let mut left = parse_condition_term(state)?;
    495
    496
        // Check for comparison operators
    497
    0
        while let Some((token, _)) = state.tokens.peek() {
    498
    0
            match token {
    499
                Token::Greater
    500
                | Token::Less
    501
                | Token::GreaterEqual
    502
                | Token::LessEqual
    503
                | Token::EqualEqual
    504
                | Token::NotEqual
    505
                | Token::AndAnd
    506
                | Token::OrOr => {
    507
    0
                    let op = expressions::token_to_binary_op(token).expect("checked: valid op");
    508
    0
                    state.tokens.advance(); // consume operator
    509
    0
                    let right = parse_condition_term(state)?;
    510
    0
                    left = Expr::new(
    511
    0
                        ExprKind::Binary {
    512
    0
                            left: Box::new(left),
    513
    0
                            op,
    514
    0
                            right: Box::new(right),
    515
    0
                        },
    516
    0
                        Span { start: 0, end: 0 },
    517
                    );
    518
                }
    519
    0
                _ => break, // Stop at closing bracket or any other token
    520
            }
    521
        }
    522
    523
    0
        Ok(left)
    524
    0
    }
    525
    526
    /// Parse a single term in a condition expression
    527
    0
    fn parse_condition_term(state: &mut ParserState) -> Result<Expr> {
    528
        // Parse a primary expression (identifier, literal, call, etc.)
    529
    0
        let mut expr = expressions::parse_prefix(state)?;
    530
    531
        // Handle postfix operations like method calls and field access
    532
    0
        while let Some((token, _)) = state.tokens.peek() {
    533
    0
            expr = match token {
    534
    0
                Token::Dot => parse_dot_operation(state, expr)?,
    535
    0
                Token::LeftParen => functions::parse_call(state, expr)?,
    536
    0
                _ => break, // Stop at other tokens
    537
            };
    538
        }
    539
    540
    0
        Ok(expr)
    541
    0
    }
    542
    543
    /// Parse dot operation (field access or method call) - complexity: 8
    544
    0
    fn parse_dot_operation(state: &mut ParserState, expr: Expr) -> Result<Expr> {
    545
    0
        state.tokens.advance(); // consume .
    546
    547
    0
        let Some((Token::Identifier(name), _)) = state.tokens.peek() else {
    548
    0
            return Ok(expr); // No identifier after dot
    549
        };
    550
    551
    0
        let name = name.clone();
    552
    0
        state.tokens.advance();
    553
    554
        // Check if it's a method call or field access
    555
    0
        if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
    556
    0
            parse_method_call(state, expr, name)
    557
        } else {
    558
    0
            Ok(create_field_access(expr, name))
    559
        }
    560
    0
    }
    561
    562
    /// Parse method call arguments (complexity: 5)
    563
    0
    fn parse_method_call(state: &mut ParserState, receiver: Expr, method: String) -> Result<Expr> {
    564
    0
        state.tokens.advance(); // consume (
    565
    566
    0
        let args = parse_method_arguments(state)?;
    567
    568
    0
        state.tokens.expect(&Token::RightParen)?;
    569
    570
    0
        Ok(Expr::new(
    571
    0
            ExprKind::MethodCall {
    572
    0
                receiver: Box::new(receiver),
    573
    0
                method,
    574
    0
                args,
    575
    0
            },
    576
    0
            Span { start: 0, end: 0 },
    577
    0
        ))
    578
    0
    }
    579
    580
    /// Parse method call arguments (complexity: 4)
    581
    0
    fn parse_method_arguments(state: &mut ParserState) -> Result<Vec<Expr>> {
    582
    0
        let mut args = Vec::new();
    583
    584
    0
        while !matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
    585
    0
            args.push(super::parse_expr_recursive(state)?);
    586
    587
    0
            if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
    588
    0
                state.tokens.advance();
    589
    0
            } else {
    590
    0
                break;
    591
            }
    592
        }
    593
    594
    0
        Ok(args)
    595
    0
    }
    596
    597
    /// Create field access expression (complexity: 1)
    598
    0
    fn create_field_access(object: Expr, field: String) -> Expr {
    599
    0
        Expr::new(
    600
    0
            ExprKind::FieldAccess {
    601
    0
                object: Box::new(object),
    602
    0
                field,
    603
    0
            },
    604
    0
            Span { start: 0, end: 0 },
    605
        )
    606
    0
    }
    607
    608
    0
    pub fn parse_list_comprehension(
    609
    0
        state: &mut ParserState,
    610
    0
        start_span: Span,
    611
    0
        element: Expr,
    612
    0
    ) -> Result<Expr> {
    613
        // We've already parsed the element expression
    614
        // Now expect: for variable in iterable [if condition]
    615
    616
    0
        state.tokens.expect(&Token::For)?;
    617
    618
        // Parse variable name
    619
    0
        let variable = if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
    620
    0
            let name = name.clone();
    621
    0
            state.tokens.advance();
    622
    0
            name
    623
        } else {
    624
    0
            bail!("Expected variable name in list comprehension");
    625
        };
    626
    627
    0
        state.tokens.expect(&Token::In)?;
    628
    629
        // Parse iterable expression
    630
    0
        let iterable = super::parse_expr_recursive(state)?;
    631
    632
        // Check for optional if condition
    633
    0
        let condition = if matches!(state.tokens.peek(), Some((Token::If, _))) {
    634
    0
            state.tokens.advance(); // consume 'if'
    635
                                    // Parse condition expression - this needs to stop at the closing bracket
    636
                                    // We'll parse a simple expression that stops at ]
    637
    0
            let cond = parse_condition_expr(state)?;
    638
    0
            Some(Box::new(cond))
    639
        } else {
    640
    0
            None
    641
        };
    642
    643
    0
        state.tokens.expect(&Token::RightBracket)?;
    644
    645
    0
        Ok(Expr::new(
    646
    0
            ExprKind::ListComprehension {
    647
    0
                element: Box::new(element),
    648
    0
                variable,
    649
    0
                iterable: Box::new(iterable),
    650
    0
                condition,
    651
    0
            },
    652
    0
            start_span,
    653
    0
        ))
    654
    0
    }
    655
    656
    /// Parse a `DataFrame` literal expression
    657
    ///
    658
    /// Parses `DataFrame` literals with column headers and data rows. The first
    659
    /// row defines column names, subsequent rows contain data values.
    660
    ///
    661
    /// # Examples
    662
    ///
    663
    /// ```
    664
    /// use ruchy::Parser;
    665
    ///
    666
    /// let input = r#"df![name => ["Alice", "Bob"], age => [30, 25]]"#;
    667
    /// let mut parser = Parser::new(input);
    668
    /// let result = parser.parse();
    669
    /// assert!(result.is_ok());
    670
    /// ```
    671
    ///
    672
    /// # Errors
    673
    ///
    674
    /// Returns an error if:
    675
    /// - Missing opening brace after `DataFrame`
    676
    /// - Invalid column name (must be identifier)
    677
    /// - Missing semicolon between rows
    678
    /// - Failed to parse data value expressions
    679
    /// - Missing closing brace
    680
    /// - Inconsistent number of values per row
    681
    /// # Errors
    682
    ///
    683
    /// Returns an error if the operation fails
    684
    /// Parse `DataFrame` header: df![ (complexity: 3)
    685
    0
    fn parse_dataframe_header(state: &mut ParserState) -> Result<Span> {
    686
    0
        let start_span = state.tokens.advance().expect("checked by parser logic").1; // consume df
    687
    0
        state.tokens.expect(&Token::Bang)?;
    688
    0
        state.tokens.expect(&Token::LeftBracket)?;
    689
    0
        Ok(start_span)
    690
    0
    }
    691
    692
    /// Parse column name identifier (complexity: 3)
    693
    0
    fn parse_dataframe_column_name(state: &mut ParserState) -> Result<String> {
    694
    0
        if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
    695
    0
            let name = name.clone();
    696
    0
            state.tokens.advance();
    697
    0
            Ok(name)
    698
        } else {
    699
    0
            bail!("Expected column name in DataFrame literal");
    700
        }
    701
    0
    }
    702
    703
    /// Parse column values after => (complexity: 4)
    704
    0
    fn parse_dataframe_column_values(state: &mut ParserState) -> Result<Vec<Expr>> {
    705
    0
        state.tokens.expect(&Token::FatArrow)?; // consume =>
    706
    707
    0
        let values = if matches!(state.tokens.peek(), Some((Token::LeftBracket, _))) {
    708
            // Values are in a list
    709
    0
            parse_list(state)?
    710
        } else {
    711
            // Parse individual expression
    712
    0
            super::parse_expr_recursive(state)?
    713
        };
    714
    715
        // Convert to vector of expressions
    716
    0
        let value_vec = match values.kind {
    717
    0
            ExprKind::List(exprs) => exprs,
    718
    0
            _ => vec![values],
    719
        };
    720
    721
    0
        Ok(value_vec)
    722
    0
    }
    723
    724
    /// Handle legacy syntax column (complexity: 3)
    725
    0
    fn handle_dataframe_legacy_syntax_column(col_name: String) -> DataFrameColumn {
    726
        // Legacy syntax: just column names, then semicolon and rows
    727
        // For backward compatibility, create empty column for now
    728
    0
        DataFrameColumn {
    729
    0
            name: col_name,
    730
    0
            values: Vec::new(),
    731
    0
        }
    732
    0
    }
    733
    734
    /// Parse column definitions loop (complexity: 6)
    735
    0
    fn parse_dataframe_column_definitions(state: &mut ParserState) -> Result<Vec<DataFrameColumn>> {
    736
    0
        let mut columns = Vec::new();
    737
    738
        loop {
    739
    0
            let col_name = parse_dataframe_column_name(state)?;
    740
    0
            parse_single_dataframe_column(state, col_name, &mut columns)?;
    741
    742
    0
            if !handle_dataframe_column_continuation(state, &mut columns)? {
    743
    0
                break;
    744
    0
            }
    745
        }
    746
    747
    0
        Ok(columns)
    748
    0
    }
    749
    750
    /// Parse a single `DataFrame` column (either new or legacy syntax) - complexity: 5
    751
    0
    fn parse_single_dataframe_column(
    752
    0
        state: &mut ParserState,
    753
    0
        col_name: String,
    754
    0
        columns: &mut Vec<DataFrameColumn>
    755
    0
    ) -> Result<()> {
    756
    0
        if matches!(state.tokens.peek(), Some((Token::FatArrow, _))) {
    757
            // New syntax: col => [values]
    758
    0
            let values = parse_dataframe_column_values(state)?;
    759
    0
            columns.push(DataFrameColumn {
    760
    0
                name: col_name,
    761
    0
                values,
    762
    0
            });
    763
    0
        } else if is_dataframe_legacy_syntax_token(state) {
    764
    0
            columns.push(handle_dataframe_legacy_syntax_column(col_name));
    765
    0
        } else {
    766
    0
            bail!("Expected '=>' or ',' after column name in DataFrame literal");
    767
        }
    768
    0
        Ok(())
    769
    0
    }
    770
    771
    /// Check if current token indicates legacy `DataFrame` syntax - complexity: 4
    772
    0
    fn is_dataframe_legacy_syntax_token(state: &mut ParserState) -> bool {
    773
    0
        matches!(state.tokens.peek(), Some((Token::Comma, _)))
    774
    0
            || matches!(state.tokens.peek(), Some((Token::Semicolon, _)))
    775
    0
            || matches!(state.tokens.peek(), Some((Token::RightBracket, _)))
    776
    0
    }
    777
    778
    /// Handle `DataFrame` column continuation tokens - complexity: 5
    779
    0
    fn handle_dataframe_column_continuation(
    780
    0
        state: &mut ParserState,
    781
    0
        columns: &mut Vec<DataFrameColumn>
    782
    0
    ) -> Result<bool> {
    783
    0
        if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
    784
    0
            state.tokens.advance();
    785
    0
            Ok(true)
    786
    0
        } else if matches!(state.tokens.peek(), Some((Token::Semicolon, _))) {
    787
            // Legacy row-based syntax
    788
    0
            state.tokens.advance();
    789
    0
            parse_legacy_dataframe_rows(state, columns)?;
    790
    0
            Ok(false)
    791
        } else {
    792
    0
            Ok(false)
    793
        }
    794
    0
    }
    795
    796
    /// Create final `DataFrame` expression (complexity: 3)
    797
    0
    fn create_dataframe_result(columns: Vec<DataFrameColumn>, start_span: Span) -> Result<Expr> {
    798
    0
        Ok(Expr::new(ExprKind::DataFrame { columns }, start_span))
    799
    0
    }
    800
    801
    /// Parse `DataFrame` literal: df![...] (complexity: 6)
    802
    0
    pub fn parse_dataframe(state: &mut ParserState) -> Result<Expr> {
    803
    0
        let start_span = parse_dataframe_header(state)?;
    804
    805
        // Check for empty DataFrame df![]
    806
    0
        if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
    807
    0
            state.tokens.advance();
    808
    0
            return create_dataframe_result(Vec::new(), start_span);
    809
    0
        }
    810
    811
        // Parse column definitions
    812
    0
        let columns = parse_dataframe_column_definitions(state)?;
    813
    814
    0
        state.tokens.expect(&Token::RightBracket)?;
    815
    0
        create_dataframe_result(columns, start_span)
    816
    0
    }
    817
    818
    /// Parse legacy row-based `DataFrame` syntax for backward compatibility
    819
    #[allow(clippy::ptr_arg)] // We need to mutate the Vec, not just read it
    820
    0
    fn parse_legacy_dataframe_rows(
    821
    0
        state: &mut ParserState,
    822
    0
        columns: &mut Vec<DataFrameColumn>,
    823
    0
    ) -> Result<()> {
    824
    0
        let rows = parse_all_dataframe_rows(state)?;
    825
    0
        populate_dataframe_columns(columns, &rows);
    826
    0
        Ok(())
    827
    0
    }
    828
    829
    /// Parse all dataframe rows (complexity: 2)
    830
    0
    fn parse_all_dataframe_rows(state: &mut ParserState) -> Result<Vec<Vec<Expr>>> {
    831
    0
        let mut rows = Vec::new();
    832
    833
        loop {
    834
    0
            if is_end_bracket(state) {
    835
    0
                break;
    836
    0
            }
    837
    838
    0
            let row = parse_single_dataframe_row(state)?;
    839
    0
            add_non_empty_row(&mut rows, row);
    840
    841
    0
            if !consume_row_separator(state) {
    842
    0
                break;
    843
    0
            }
    844
        }
    845
    846
    0
        Ok(rows)
    847
    0
    }
    848
    849
    /// Check if current token is end bracket (complexity: 1)
    850
    0
    fn is_end_bracket(state: &mut ParserState) -> bool {
    851
    0
        matches!(state.tokens.peek(), Some((Token::RightBracket, _)))
    852
    0
    }
    853
    854
    /// Parse a single dataframe row (complexity: 2)
    855
    0
    fn parse_single_dataframe_row(state: &mut ParserState) -> Result<Vec<Expr>> {
    856
    0
        let mut row = Vec::new();
    857
    858
        loop {
    859
    0
            if is_row_boundary(state) {
    860
    0
                break;
    861
    0
            }
    862
    863
    0
            row.push(super::parse_expr_recursive(state)?);
    864
    865
    0
            if !consume_value_separator(state) {
    866
    0
                break;
    867
    0
            }
    868
        }
    869
    870
    0
        Ok(row)
    871
    0
    }
    872
    873
    /// Check if current token is a row boundary (complexity: 2)
    874
    0
    fn is_row_boundary(state: &mut ParserState) -> bool {
    875
    0
        matches!(state.tokens.peek(), Some((Token::Semicolon, _)))
    876
    0
            || matches!(state.tokens.peek(), Some((Token::RightBracket, _)))
    877
    0
    }
    878
    879
    /// Consume comma separator if present (complexity: 2)
    880
    0
    fn consume_value_separator(state: &mut ParserState) -> bool {
    881
    0
        if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
    882
    0
            state.tokens.advance();
    883
    0
            true
    884
        } else {
    885
    0
            false
    886
        }
    887
    0
    }
    888
    889
    /// Consume semicolon row separator if present (complexity: 2)
    890
    0
    fn consume_row_separator(state: &mut ParserState) -> bool {
    891
    0
        if matches!(state.tokens.peek(), Some((Token::Semicolon, _))) {
    892
    0
            state.tokens.advance();
    893
    0
            true
    894
        } else {
    895
    0
            false
    896
        }
    897
    0
    }
    898
    899
    /// Add non-empty row to collection (complexity: 2)
    900
    0
    fn add_non_empty_row(rows: &mut Vec<Vec<Expr>>, row: Vec<Expr>) {
    901
    0
        if !row.is_empty() {
    902
    0
            rows.push(row);
    903
    0
        }
    904
    0
    }
    905
    906
    /// Populate columns from row data (complexity: 3)
    907
    0
    fn populate_dataframe_columns(columns: &mut [DataFrameColumn], rows: &[Vec<Expr>]) {
    908
    0
        for (col_idx, column) in columns.iter_mut().enumerate() {
    909
    0
            for row in rows {
    910
    0
                if col_idx < row.len() {
    911
    0
                    column.values.push(row[col_idx].clone());
    912
    0
                }
    913
            }
    914
        }
    915
    0
    }