## 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/backend/transpiler/expressions.rs

    Line
    Count
    Source
    1
    //! Expression transpilation methods
    2
    3
    #![allow(clippy::missing_errors_doc)]
    4
    #![allow(clippy::needless_pass_by_value)] // TokenStream by value is intentional for quote! macro
    5
    6
    use super::Transpiler;
    7
    use crate::frontend::ast::{BinaryOp::{self, NullCoalesce}, Expr, ExprKind, Literal, StringPart, UnaryOp};
    8
    use anyhow::{bail, Result};
    9
    use proc_macro2::TokenStream;
    10
    use quote::{format_ident, quote};
    11
    12
    impl Transpiler {
    13
        /// Transpiles literal values
    14
    0
        pub fn transpile_literal(lit: &Literal) -> TokenStream {
    15
    0
            match lit {
    16
    0
                Literal::Integer(i) => Self::transpile_integer(*i),
    17
    0
                Literal::Float(f) => quote! { #f },
    18
    0
                Literal::Unit => quote! { () },
    19
    0
                _ => Self::transpile_simple_literal(lit),
    20
            }
    21
    0
        }
    22
    23
    0
        fn transpile_simple_literal(lit: &Literal) -> TokenStream {
    24
    0
            match lit {
    25
    0
                Literal::String(s) => quote! { #s },
    26
    0
                Literal::Bool(b) => quote! { #b },
    27
    0
                Literal::Char(c) => quote! { #c },
    28
    0
                _ => unreachable!(),
    29
            }
    30
    0
        }
    31
    32
    0
        fn transpile_integer(i: i64) -> TokenStream {
    33
            // Integer literals in Rust need proper type handling
    34
            // Use i32 for values that fit, i64 otherwise
    35
    0
            if let Ok(i32_val) = i32::try_from(i) {
    36
                // Use i32 suffix for clarity and to match struct field types
    37
    0
                let literal = proc_macro2::Literal::i32_suffixed(i32_val);
    38
    0
                quote! { #literal }
    39
            } else {
    40
                // For large integers, we need i64 suffix
    41
    0
                let literal = proc_macro2::Literal::i64_suffixed(i);
    42
    0
                quote! { #literal }
    43
            }
    44
    0
        }
    45
    46
        /// Transpiles string interpolation
    47
        ///
    48
        /// # Errors
    49
        /// Returns an error if expression transpilation fails
    50
    0
        pub fn transpile_string_interpolation(&self, parts: &[StringPart]) -> Result<TokenStream> {
    51
    0
            if parts.is_empty() {
    52
    0
                return Ok(quote! { "" });
    53
    0
            }
    54
    55
    0
            let mut format_string = String::new();
    56
    0
            let mut args = Vec::new();
    57
    58
    0
            for part in parts {
    59
    0
                match part {
    60
    0
                    StringPart::Text(s) => {
    61
    0
                        // Escape any format specifiers in literal parts
    62
    0
                        format_string.push_str(&s.replace('{', "{{").replace('}', "}}"));
    63
    0
                    }
    64
    0
                    StringPart::Expr(expr) => {
    65
    0
                        format_string.push_str("{}");
    66
    0
                        let expr_tokens = self.transpile_expr(expr)?;
    67
    0
                        args.push(expr_tokens);
    68
                    }
    69
    0
                    StringPart::ExprWithFormat { expr, format_spec } => {
    70
                        // Include the format specifier in the format string
    71
    0
                        format_string.push('{');
    72
    0
                        format_string.push_str(format_spec);
    73
    0
                        format_string.push('}');
    74
    0
                        let expr_tokens = self.transpile_expr(expr)?;
    75
    0
                        args.push(expr_tokens);
    76
                    }
    77
                }
    78
            }
    79
    80
    0
            Ok(quote! {
    81
                format!(#format_string #(, #args)*)
    82
            })
    83
    0
        }
    84
    85
        /// Transpiles binary operations
    86
    0
        pub fn transpile_binary(&self, left: &Expr, op: BinaryOp, right: &Expr) -> Result<TokenStream> {
    87
            // Special handling for string concatenation
    88
            // Only treat as string concatenation if at least one operand is definitely a string
    89
    0
            if op == BinaryOp::Add && (Self::is_definitely_string(left) || Self::is_definitely_string(right)) {
    90
    0
                return self.transpile_string_concatenation(left, right);
    91
    0
            }
    92
    93
            // Transpile operands with precedence-aware parentheses
    94
    0
            let left_tokens = self.transpile_expr_with_precedence(left, op, true)?;
    95
    0
            let right_tokens = self.transpile_expr_with_precedence(right, op, false)?;
    96
    97
    0
            Ok(Self::transpile_binary_op(left_tokens, op, right_tokens))
    98
    0
        }
    99
    100
        /// Transpile expression with precedence-aware parentheses
    101
        ///
    102
        /// Adds parentheses around sub-expressions when needed to preserve precedence
    103
    0
        fn transpile_expr_with_precedence(&self, expr: &Expr, parent_op: BinaryOp, is_left_operand: bool) -> Result<TokenStream> {
    104
    0
            let tokens = self.transpile_expr(expr)?;
    105
    106
            // Check if we need parentheses
    107
    0
            if let ExprKind::Binary { op: child_op, .. } = &expr.kind {
    108
    0
                let parent_prec = Self::get_operator_precedence(parent_op);
    109
    0
                let child_prec = Self::get_operator_precedence(*child_op);
    110
    111
                // Add parentheses if child has lower precedence
    112
                // For right operands, also add parentheses if precedence is equal and parent is right-associative
    113
    0
                let needs_parens = child_prec < parent_prec ||
    114
    0
                    (!is_left_operand && child_prec == parent_prec && Self::is_right_associative(parent_op));
    115
    116
    0
                if needs_parens {
    117
    0
                    return Ok(quote! { (#tokens) });
    118
    0
                }
    119
    0
            }
    120
    121
    0
            Ok(tokens)
    122
    0
        }
    123
    124
        /// Get operator precedence (higher number = higher precedence)
    125
    0
        fn get_operator_precedence(op: BinaryOp) -> i32 {
    126
    0
            match op {
    127
    0
                BinaryOp::Or => 10,
    128
    0
                BinaryOp::And => 20,
    129
    0
                BinaryOp::Equal | BinaryOp::NotEqual => 30,
    130
    0
                BinaryOp::Less | BinaryOp::LessEqual | BinaryOp::Greater | BinaryOp::GreaterEqual => 40,
    131
    0
                BinaryOp::Add | BinaryOp::Subtract => 50,
    132
    0
                BinaryOp::Multiply | BinaryOp::Divide | BinaryOp::Modulo => 60,
    133
    0
                BinaryOp::Power => 70,
    134
    0
                _ => 0, // Default for other operators
    135
            }
    136
    0
        }
    137
    138
        /// Check if operator is right-associative
    139
    0
        fn is_right_associative(op: BinaryOp) -> bool {
    140
    0
            matches!(op, BinaryOp::Power) // Only power is right-associative in most languages
    141
    0
        }
    142
    143
    0
        fn transpile_binary_op(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    144
            use BinaryOp::{
    145
                Add, And, BitwiseAnd, BitwiseOr, BitwiseXor, Divide, Equal, Greater, GreaterEqual,
    146
                LeftShift, Less, LessEqual, Modulo, Multiply, NotEqual, Or, Power,
    147
                Subtract,
    148
            };
    149
    0
            match op {
    150
                // Arithmetic operations
    151
                Add | Subtract | Multiply | Divide | Modulo | Power => {
    152
    0
                    Self::transpile_arithmetic_op(left, op, right)
    153
                }
    154
                // Comparison operations
    155
                Equal | NotEqual | Less | LessEqual | Greater | GreaterEqual => {
    156
    0
                    Self::transpile_comparison_op(left, op, right)
    157
                }
    158
                // Logical operations
    159
    0
                And | Or | NullCoalesce => Self::transpile_logical_op(left, op, right),
    160
                // Bitwise operations
    161
                BitwiseAnd | BitwiseOr | BitwiseXor | LeftShift => {
    162
    0
                    Self::transpile_bitwise_op(left, op, right)
    163
                }
    164
            }
    165
    0
        }
    166
    167
    0
        fn transpile_arithmetic_op(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    168
            use BinaryOp::{Add, Divide, Modulo, Multiply, Power, Subtract};
    169
    0
            match op {
    170
                Add | Subtract | Multiply | Divide | Modulo => {
    171
    0
                    Self::transpile_basic_arithmetic(left, op, right)
    172
                }
    173
    0
                Power => quote! { #left.pow(#right) },
    174
    0
                _ => unreachable!(),
    175
            }
    176
    0
        }
    177
    178
    0
        fn transpile_basic_arithmetic(
    179
    0
            left: TokenStream,
    180
    0
            op: BinaryOp,
    181
    0
            right: TokenStream,
    182
    0
        ) -> TokenStream {
    183
            // Reduce complexity by splitting into smaller functions
    184
    0
            match op {
    185
    0
                BinaryOp::Add => quote! { #left + #right },
    186
    0
                BinaryOp::Subtract => quote! { #left - #right },
    187
    0
                BinaryOp::Multiply => quote! { #left * #right },
    188
    0
                _ => Self::transpile_division_mod(left, op, right),
    189
            }
    190
    0
        }
    191
    192
    0
        fn transpile_division_mod(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    193
    0
            match op {
    194
    0
                BinaryOp::Divide => quote! { #left / #right },
    195
    0
                BinaryOp::Modulo => quote! { #left % #right },
    196
    0
                _ => unreachable!(),
    197
            }
    198
    0
        }
    199
    200
    0
        fn transpile_comparison_op(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    201
            use BinaryOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
    202
    0
            match op {
    203
    0
                Equal | NotEqual => Self::transpile_equality(left, op, right),
    204
    0
                Less | LessEqual | Greater | GreaterEqual => Self::transpile_ordering(left, op, right),
    205
    0
                _ => unreachable!(),
    206
            }
    207
    0
        }
    208
    209
    0
        fn transpile_equality(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    210
    0
            match op {
    211
    0
                BinaryOp::Equal => quote! { #left == #right },
    212
    0
                BinaryOp::NotEqual => quote! { #left != #right },
    213
    0
                _ => unreachable!(),
    214
            }
    215
    0
        }
    216
    217
    0
        fn transpile_ordering(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    218
    0
            match op {
    219
    0
                BinaryOp::Less => quote! { #left < #right },
    220
    0
                BinaryOp::LessEqual => quote! { #left <= #right },
    221
    0
                _ => Self::transpile_greater_ops(left, op, right),
    222
            }
    223
    0
        }
    224
    225
    0
        fn transpile_greater_ops(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    226
    0
            match op {
    227
    0
                BinaryOp::Greater => quote! { #left > #right },
    228
    0
                BinaryOp::GreaterEqual => quote! { #left >= #right },
    229
    0
                _ => unreachable!(),
    230
            }
    231
    0
        }
    232
    233
    0
        fn transpile_logical_op(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    234
    0
            match op {
    235
    0
                BinaryOp::And => quote! { #left && #right },
    236
    0
                BinaryOp::Or => quote! { #left || #right },
    237
    0
                _ => unreachable!(),
    238
            }
    239
    0
        }
    240
    241
    0
        fn transpile_bitwise_op(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    242
            use BinaryOp::{BitwiseAnd, BitwiseOr, BitwiseXor};
    243
    0
            match op {
    244
    0
                BitwiseAnd => quote! { #left & #right },
    245
    0
                BitwiseOr => quote! { #left | #right },
    246
    0
                BitwiseXor => quote! { #left ^ #right },
    247
    0
                _ => Self::transpile_shift_ops(left, op, right),
    248
            }
    249
    0
        }
    250
    251
    0
        fn transpile_shift_ops(left: TokenStream, op: BinaryOp, right: TokenStream) -> TokenStream {
    252
    0
            match op {
    253
    0
                BinaryOp::LeftShift => quote! { #left << #right },
    254
    0
                _ => unreachable!(),
    255
            }
    256
    0
        }
    257
    258
        /// Transpiles unary operations
    259
    0
        pub fn transpile_unary(&self, op: UnaryOp, operand: &Expr) -> Result<TokenStream> {
    260
    0
            let operand_tokens = self.transpile_expr(operand)?;
    261
    262
    0
            Ok(match op {
    263
    0
                UnaryOp::Not | UnaryOp::BitwiseNot => quote! { !#operand_tokens },
    264
    0
                UnaryOp::Negate => quote! { -#operand_tokens },
    265
    0
                UnaryOp::Reference => quote! { &#operand_tokens },
    266
            })
    267
    0
        }
    268
    269
    270
        /// Transpiles await expressions
    271
    0
        pub fn transpile_await(&self, expr: &Expr) -> Result<TokenStream> {
    272
    0
            let expr_tokens = self.transpile_expr(expr)?;
    273
    0
            Ok(quote! { #expr_tokens.await })
    274
    0
        }
    275
    276
        /// Transpiles async blocks
    277
    0
        pub fn transpile_async_block(&self, body: &Expr) -> Result<TokenStream> {
    278
    0
            let body_tokens = self.transpile_expr(body)?;
    279
    0
            Ok(quote! { async { #body_tokens } })
    280
    0
        }
    281
    282
        /// Transpiles throw expressions (panic in Rust)
    283
    0
        pub fn transpile_throw(&self, expr: &Expr) -> Result<TokenStream> {
    284
    0
            let expr_tokens = self.transpile_expr(expr)?;
    285
    0
            Ok(quote! {
    286
    0
                panic!(#expr_tokens)
    287
    0
            })
    288
    0
        }
    289
    290
        /// Transpiles field access
    291
    0
        pub fn transpile_field_access(&self, object: &Expr, field: &str) -> Result<TokenStream> {
    292
            use crate::frontend::ast::ExprKind;
    293
    294
    0
            let obj_tokens = self.transpile_expr(object)?;
    295
    296
            // Check if the object is an ObjectLiteral (HashMap) or module path
    297
    0
            match &object.kind {
    298
                ExprKind::ObjectLiteral { .. } => {
    299
                    // Direct object literal access - use get()
    300
    0
                    Ok(quote! {
    301
    0
                        #obj_tokens.get(#field)
    302
    0
                            .cloned()
    303
    0
                            .unwrap_or_else(|| panic!("Field '{}' not found", #field))
    304
    0
                    })
    305
                }
    306
                ExprKind::FieldAccess { .. } => {
    307
                    // Nested field access like net::TcpListener - use :: syntax
    308
    0
                    let field_ident = format_ident!("{}", field);
    309
    0
                    Ok(quote! { #obj_tokens::#field_ident })
    310
                }
    311
    0
                ExprKind::Identifier(name) if name.contains("::") => {
    312
                    // Module path identifier - use :: syntax
    313
    0
                    let field_ident = format_ident!("{}", field);
    314
    0
                    Ok(quote! { #obj_tokens::#field_ident })
    315
                }
    316
                _ => {
    317
                    // For other cases, assume HashMap access
    318
    0
                    Ok(quote! {
    319
    0
                        #obj_tokens.get(#field)
    320
    0
                            .cloned()
    321
    0
                            .unwrap_or_else(|| panic!("Field '{}' not found", #field))
    322
    0
                    })
    323
                }
    324
            }
    325
    0
        }
    326
    327
        /// Transpiles index access `(array[index])`
    328
    0
        pub fn transpile_index_access(&self, object: &Expr, index: &Expr) -> Result<TokenStream> {
    329
            use crate::frontend::ast::{ExprKind, Literal};
    330
    331
    0
            let obj_tokens = self.transpile_expr(object)?;
    332
    0
            let index_tokens = self.transpile_expr(index)?;
    333
    334
            // Smart index access: HashMap.get() for string keys, array indexing for numeric
    335
    0
            match &index.kind {
    336
                // String literal keys use HashMap.get()
    337
                ExprKind::Literal(Literal::String(_)) => {
    338
    0
                    Ok(quote! {
    339
    0
                        #obj_tokens.get(#index_tokens)
    340
    0
                            .cloned()
    341
    0
                            .unwrap_or_else(|| panic!("Key not found"))
    342
    0
                    })
    343
                }
    344
                // Numeric and other keys use array indexing
    345
                _ => {
    346
    0
                    Ok(quote! { #obj_tokens[#index_tokens as usize] })
    347
                }
    348
            }
    349
    0
        }
    350
    351
        /// Transpiles slice access `(array[start:end])`
    352
    0
        pub fn transpile_slice(&self, object: &Expr, start: Option<&Expr>, end: Option<&Expr>) -> Result<TokenStream> {
    353
    0
            let obj_tokens = self.transpile_expr(object)?;
    354
    355
    0
            match (start, end) {
    356
                (None, None) => {
    357
                    // Full slice [..]
    358
    0
                    Ok(quote! { &#obj_tokens[..] })
    359
                }
    360
    0
                (None, Some(end)) => {
    361
                    // Slice from beginning [..end]
    362
    0
                    let end_tokens = self.transpile_expr(end)?;
    363
    0
                    Ok(quote! { &#obj_tokens[..#end_tokens as usize] })
    364
                }
    365
    0
                (Some(start), None) => {
    366
                    // Slice to end [start..]
    367
    0
                    let start_tokens = self.transpile_expr(start)?;
    368
    0
                    Ok(quote! { &#obj_tokens[#start_tokens as usize..] })
    369
                }
    370
    0
                (Some(start), Some(end)) => {
    371
                    // Full range slice [start..end]
    372
    0
                    let start_tokens = self.transpile_expr(start)?;
    373
    0
                    let end_tokens = self.transpile_expr(end)?;
    374
    0
                    Ok(quote! { &#obj_tokens[#start_tokens as usize..#end_tokens as usize] })
    375
                }
    376
            }
    377
    0
        }
    378
    379
        /// Transpiles assignment
    380
    0
        pub fn transpile_assign(&self, target: &Expr, value: &Expr) -> Result<TokenStream> {
    381
    0
            let target_tokens = self.transpile_expr(target)?;
    382
    0
            let value_tokens = self.transpile_expr(value)?;
    383
    0
            Ok(quote! { #target_tokens = #value_tokens })
    384
    0
        }
    385
    386
        /// Transpiles compound assignment
    387
    0
        pub fn transpile_compound_assign(
    388
    0
            &self,
    389
    0
            target: &Expr,
    390
    0
            op: BinaryOp,
    391
    0
            value: &Expr,
    392
    0
        ) -> Result<TokenStream> {
    393
    0
            let target_tokens = self.transpile_expr(target)?;
    394
    0
            let value_tokens = self.transpile_expr(value)?;
    395
    0
            let op_tokens = Self::get_compound_op_token(op)?;
    396
    397
    0
            Ok(quote! { #target_tokens #op_tokens #value_tokens })
    398
    0
        }
    399
    400
    0
        fn get_compound_op_token(op: BinaryOp) -> Result<TokenStream> {
    401
            use BinaryOp::{Add, Divide, Modulo, Multiply, Subtract};
    402
    0
            match op {
    403
    0
                Add | Subtract | Multiply => Ok(Self::get_basic_compound_token(op)),
    404
    0
                Divide | Modulo => Ok(Self::get_division_compound_token(op)),
    405
                _ => {
    406
                    use anyhow::bail;
    407
    0
                    bail!("Invalid operator for compound assignment: {:?}", op)
    408
                }
    409
            }
    410
    0
        }
    411
    412
    0
        fn get_basic_compound_token(op: BinaryOp) -> TokenStream {
    413
    0
            match op {
    414
    0
                BinaryOp::Add => quote! { += },
    415
    0
                BinaryOp::Subtract => quote! { -= },
    416
    0
                BinaryOp::Multiply => quote! { *= },
    417
    0
                _ => unreachable!(),
    418
            }
    419
    0
        }
    420
    421
    0
        fn get_division_compound_token(op: BinaryOp) -> TokenStream {
    422
    0
            match op {
    423
    0
                BinaryOp::Divide => quote! { /= },
    424
    0
                BinaryOp::Modulo => quote! { %= },
    425
    0
                _ => unreachable!(),
    426
            }
    427
    0
        }
    428
    429
        /// Transpiles pre-increment
    430
    0
        pub fn transpile_pre_increment(&self, target: &Expr) -> Result<TokenStream> {
    431
    0
            let target_tokens = self.transpile_expr(target)?;
    432
    0
            Ok(quote! { { #target_tokens += 1; #target_tokens } })
    433
    0
        }
    434
    435
        /// Transpiles post-increment
    436
    0
        pub fn transpile_post_increment(&self, target: &Expr) -> Result<TokenStream> {
    437
    0
            let target_tokens = self.transpile_expr(target)?;
    438
    0
            Ok(quote! {
    439
    0
                {
    440
    0
                    let _tmp = #target_tokens;
    441
    0
                    #target_tokens += 1;
    442
    0
                    _tmp
    443
    0
                }
    444
    0
            })
    445
    0
        }
    446
    447
        /// Transpiles pre-decrement
    448
    0
        pub fn transpile_pre_decrement(&self, target: &Expr) -> Result<TokenStream> {
    449
    0
            let target_tokens = self.transpile_expr(target)?;
    450
    0
            Ok(quote! { { #target_tokens -= 1; #target_tokens } })
    451
    0
        }
    452
    453
        /// Transpiles post-decrement
    454
    0
        pub fn transpile_post_decrement(&self, target: &Expr) -> Result<TokenStream> {
    455
    0
            let target_tokens = self.transpile_expr(target)?;
    456
    0
            Ok(quote! {
    457
    0
                {
    458
    0
                    let _tmp = #target_tokens;
    459
    0
                    #target_tokens -= 1;
    460
    0
                    _tmp
    461
    0
                }
    462
    0
            })
    463
    0
        }
    464
    465
        /// Transpiles list literals
    466
    0
        pub fn transpile_list(&self, elements: &[Expr]) -> Result<TokenStream> {
    467
            // Check if any elements are spread expressions
    468
    0
            let has_spread = elements.iter().any(|e| matches!(e.kind, crate::frontend::ast::ExprKind::Spread { .. }));
    469
    470
    0
            if has_spread {
    471
                // Handle spread expressions by building vector with extends
    472
    0
                let mut statements = Vec::new();
    473
    0
                statements.push(quote! { let mut __temp_vec = Vec::new(); });
    474
    475
    0
                for element in elements {
    476
    0
                    if let crate::frontend::ast::ExprKind::Spread { expr } = &element.kind {
    477
    0
                        let expr_tokens = self.transpile_expr(expr)?;
    478
    0
                        statements.push(quote! { __temp_vec.extend(#expr_tokens); });
    479
                    } else {
    480
    0
                        let expr_tokens = self.transpile_expr(element)?;
    481
    0
                        statements.push(quote! { __temp_vec.push(#expr_tokens); });
    482
                    }
    483
                }
    484
    485
    0
                statements.push(quote! { __temp_vec });
    486
    0
                Ok(quote! { { #(#statements)* } })
    487
            } else {
    488
                // No spread expressions, use simple vec![] macro
    489
    0
                let element_tokens: Result<Vec<_>> =
    490
    0
                    elements.iter().map(|e| self.transpile_expr(e)).collect();
    491
    0
                let element_tokens = element_tokens?;
    492
    0
                Ok(quote! { vec![#(#element_tokens),*] })
    493
            }
    494
    0
        }
    495
    496
        /// Transpiles tuple literals
    497
    0
        pub fn transpile_tuple(&self, elements: &[Expr]) -> Result<TokenStream> {
    498
    0
            let element_tokens: Result<Vec<_>> =
    499
    0
                elements.iter().map(|e| self.transpile_expr(e)).collect();
    500
    0
            let element_tokens = element_tokens?;
    501
    0
            Ok(quote! { (#(#element_tokens),*) })
    502
    0
        }
    503
    504
        /// Transpiles range expressions
    505
    0
        pub fn transpile_range(
    506
    0
            &self,
    507
    0
            start: &Expr,
    508
    0
            end: &Expr,
    509
    0
            inclusive: bool,
    510
    0
        ) -> Result<TokenStream> {
    511
    0
            let start_tokens = self.transpile_expr(start)?;
    512
    0
            let end_tokens = self.transpile_expr(end)?;
    513
    514
    0
            if inclusive {
    515
    0
                Ok(quote! { #start_tokens..=#end_tokens })
    516
            } else {
    517
    0
                Ok(quote! { #start_tokens..#end_tokens })
    518
            }
    519
    0
        }
    520
    521
        /// Transpiles object literals
    522
    0
        pub fn transpile_object_literal(
    523
    0
            &self,
    524
    0
            fields: &[crate::frontend::ast::ObjectField],
    525
    0
        ) -> Result<TokenStream> {
    526
    0
            let field_tokens = self.collect_hashmap_field_tokens(fields)?;
    527
    0
            Ok(quote! {
    528
                {
    529
                    let mut map: std::collections::HashMap<String, String> = std::collections::HashMap::new();
    530
                    #(#field_tokens)*
    531
                    map
    532
                }
    533
            })
    534
    0
        }
    535
    536
    0
        fn collect_hashmap_field_tokens(
    537
    0
            &self,
    538
    0
            fields: &[crate::frontend::ast::ObjectField],
    539
    0
        ) -> Result<Vec<TokenStream>> {
    540
            use crate::frontend::ast::ObjectField;
    541
    0
            let mut field_tokens = Vec::new();
    542
    543
    0
            for field in fields {
    544
    0
                let token = match field {
    545
    0
                    ObjectField::KeyValue { key, value } => {
    546
    0
                        let value_tokens = self.transpile_expr(value)?;
    547
    0
                        quote! { map.insert(#key.to_string(), (#value_tokens).to_string()); }
    548
                    }
    549
    0
                    ObjectField::Spread { expr } => {
    550
    0
                        let expr_tokens = self.transpile_expr(expr)?;
    551
                        // For spread syntax, merge the other map into this one
    552
    0
                        quote! {
    553
                            for (k, v) in #expr_tokens {
    554
                                map.insert(k, v);
    555
                            }
    556
                        }
    557
                    }
    558
                };
    559
    0
                field_tokens.push(token);
    560
            }
    561
    562
    0
            Ok(field_tokens)
    563
    0
        }
    564
    565
    566
        /// Transpiles struct literals
    567
    0
        pub fn transpile_struct_literal(
    568
    0
            &self,
    569
    0
            name: &str,
    570
    0
            fields: &[(String, Expr)],
    571
    0
        ) -> Result<TokenStream> {
    572
    0
            let struct_name = format_ident!("{}", name);
    573
    0
            let mut field_tokens = Vec::new();
    574
    575
    0
            for (field_name, value) in fields {
    576
    0
                let field_ident = format_ident!("{}", field_name);
    577
    0
                let value_tokens = match &value.kind {
    578
                    // Convert string literals to String for struct fields
    579
    0
                    ExprKind::Literal(Literal::String(s)) => {
    580
    0
                        quote! { #s.to_string() }
    581
                    }
    582
    0
                    _ => self.transpile_expr(value)?,
    583
                };
    584
    0
                field_tokens.push(quote! { #field_ident: #value_tokens });
    585
            }
    586
    587
    0
            Ok(quote! {
    588
                #struct_name {
    589
                    #(#field_tokens,)*
    590
                }
    591
            })
    592
    0
        }
    593
    594
        /// Check if an expression is definitely a string (conservative detection)
    595
    0
        fn is_definitely_string(expr: &Expr) -> bool {
    596
    0
            match &expr.kind {
    597
                // String literals are definitely strings
    598
    0
                ExprKind::Literal(Literal::String(_)) => true,
    599
                // String interpolation is definitely strings
    600
    0
                ExprKind::StringInterpolation { .. } => true,
    601
                // Binary expressions with + that involve strings are string concatenations
    602
    0
                ExprKind::Binary { op: BinaryOp::Add, left, right } => {
    603
    0
                    Self::is_definitely_string(left) || Self::is_definitely_string(right)
    604
                },
    605
                // Method calls on strings that return strings
    606
    0
                ExprKind::MethodCall { receiver, method, .. } => {
    607
    0
                    matches!(method.as_str(), "to_string" | "trim" | "to_uppercase" | "to_lowercase") ||
    608
    0
                    Self::is_definitely_string(receiver)
    609
                },
    610
                // Variables could be strings, but we can't be sure without type info
    611
                // For now, be conservative and don't assume variables are strings
    612
    0
                ExprKind::Identifier(_) => false,
    613
                // Function calls are NOT definitely strings - they could return any type
    614
    0
                ExprKind::Call { .. } => false,
    615
                // Other expressions are not strings
    616
    0
                _ => false,
    617
            }
    618
    0
        }
    619
    620
    621
        /// Transpile string concatenation using proper Rust string operations
    622
    0
        fn transpile_string_concatenation(&self, left: &Expr, right: &Expr) -> Result<TokenStream> {
    623
    0
            let left_tokens = self.transpile_expr(left)?;
    624
    0
            let right_tokens = self.transpile_expr(right)?;
    625
    626
            // Use format! with proper string handling - convert both to strings to avoid type mismatches
    627
            // This avoids the String + String issue in Rust by using format! exclusively
    628
    0
            Ok(quote! { format!("{}{}", #left_tokens, #right_tokens) })
    629
    0
        }
    630
    }
    631
    632
    #[cfg(test)]
    633
    #[allow(clippy::single_char_pattern)]
    634
    mod tests {
    635
        use super::*;
    636
        use crate::{Parser, frontend::ast::ExprKind};
    637
    638
        fn create_transpiler() -> Transpiler {
    639
            Transpiler::new()
    640
        }
    641
    642
        #[test]
    643
        fn test_transpile_integer_literal() {
    644
            let transpiler = create_transpiler();
    645
            let code = "42";
    646
            let mut parser = Parser::new(code);
    647
            let ast = parser.parse().unwrap();
    648
    649
            let result = transpiler.transpile(&ast).unwrap();
    650
            let rust_str = result.to_string();
    651
    652
            assert!(rust_str.contains("42"));
    653
        }
    654
    655
        #[test]
    656
        fn test_transpile_float_literal() {
    657
            let transpiler = create_transpiler();
    658
            let code = "3.14";
    659
            let mut parser = Parser::new(code);
    660
            let ast = parser.parse().unwrap();
    661
    662
            let result = transpiler.transpile(&ast).unwrap();
    663
            let rust_str = result.to_string();
    664
    665
            assert!(rust_str.contains("3.14"));
    666
        }
    667
    668
        #[test]
    669
        fn test_transpile_string_literal() {
    670
            let transpiler = create_transpiler();
    671
            let code = "\"hello\"";
    672
            let mut parser = Parser::new(code);
    673
            let ast = parser.parse().unwrap();
    674
    675
            let result = transpiler.transpile(&ast).unwrap();
    676
            let rust_str = result.to_string();
    677
    678
            assert!(rust_str.contains("hello"));
    679
        }
    680
    681
        #[test]
    682
        fn test_transpile_boolean_literal() {
    683
            let transpiler = create_transpiler();
    684
            let code = "true";
    685
            let mut parser = Parser::new(code);
    686
            let ast = parser.parse().unwrap();
    687
    688
            let result = transpiler.transpile(&ast).unwrap();
    689
            let rust_str = result.to_string();
    690
    691
            assert!(rust_str.contains("true"));
    692
        }
    693
    694
        #[test]
    695
        fn test_transpile_unit_literal() {
    696
            let transpiler = create_transpiler();
    697
            let code = "()";
    698
            let mut parser = Parser::new(code);
    699
            let ast = parser.parse().unwrap();
    700
    701
            let result = transpiler.transpile(&ast).unwrap();
    702
            let rust_str = result.to_string();
    703
    704
            assert!(rust_str.contains("()"));
    705
        }
    706
    707
        #[test]
    708
        fn test_transpile_binary_addition() {
    709
            let transpiler = create_transpiler();
    710
            let code = "5 + 3";
    711
            let mut parser = Parser::new(code);
    712
            let ast = parser.parse().unwrap();
    713
    714
            let result = transpiler.transpile(&ast).unwrap();
    715
            let rust_str = result.to_string();
    716
    717
            assert!(rust_str.contains("5") && rust_str.contains("3"));
    718
            assert!(rust_str.contains("+"));
    719
        }
    720
    721
        #[test]
    722
        fn test_transpile_binary_subtraction() {
    723
            let transpiler = create_transpiler();
    724
            let code = "10 - 4";
    725
            let mut parser = Parser::new(code);
    726
            let ast = parser.parse().unwrap();
    727
    728
            let result = transpiler.transpile(&ast).unwrap();
    729
            let rust_str = result.to_string();
    730
    731
            assert!(rust_str.contains("10") && rust_str.contains("4"));
    732
            assert!(rust_str.contains("-"));
    733
        }
    734
    735
        #[test]
    736
        fn test_transpile_binary_multiplication() {
    737
            let transpiler = create_transpiler();
    738
            let code = "6 * 7";
    739
            let mut parser = Parser::new(code);
    740
            let ast = parser.parse().unwrap();
    741
    742
            let result = transpiler.transpile(&ast).unwrap();
    743
            let rust_str = result.to_string();
    744
    745
            assert!(rust_str.contains("6") && rust_str.contains("7"));
    746
            assert!(rust_str.contains("*"));
    747
        }
    748
    749
        #[test]
    750
        fn test_transpile_binary_division() {
    751
            let transpiler = create_transpiler();
    752
            let code = "15 / 3";
    753
            let mut parser = Parser::new(code);
    754
            let ast = parser.parse().unwrap();
    755
    756
            let result = transpiler.transpile(&ast).unwrap();
    757
            let rust_str = result.to_string();
    758
    759
            assert!(rust_str.contains("15") && rust_str.contains("3"));
    760
            assert!(rust_str.contains("/"));
    761
        }
    762
    763
        #[test]
    764
        fn test_transpile_binary_modulo() {
    765
            let transpiler = create_transpiler();
    766
            let code = "10 % 3";
    767
            let mut parser = Parser::new(code);
    768
            let ast = parser.parse().unwrap();
    769
    770
            let result = transpiler.transpile(&ast).unwrap();
    771
            let rust_str = result.to_string();
    772
    773
            assert!(rust_str.contains("10") && rust_str.contains("3"));
    774
            assert!(rust_str.contains("%"));
    775
        }
    776
    777
        // Note: String concatenation test removed due to parser limitations with string + operator
    778
    779
        #[test]
    780
        fn test_transpile_comparison_operators() {
    781
            let operators = vec!["<", ">", "<=", ">=", "==", "!="];
    782
    783
            for op in operators {
    784
                let transpiler = create_transpiler();
    785
                let code = format!("5 {op} 3");
    786
                let mut parser = Parser::new(&code);
    787
                let ast = parser.parse().unwrap();
    788
    789
                let result = transpiler.transpile(&ast).unwrap();
    790
                let rust_str = result.to_string();
    791
    792
                assert!(rust_str.contains("5") && rust_str.contains("3"),
    793
                       "Failed for operator {op}: {rust_str}");
    794
            }
    795
        }
    796
    797
        #[test]
    798
        fn test_transpile_logical_operators() {
    799
            let operators = vec!["&&", "||"];
    800
    801
            for op in operators {
    802
                let transpiler = create_transpiler();
    803
                let code = format!("true {op} false");
    804
                let mut parser = Parser::new(&code);
    805
                let ast = parser.parse().unwrap();
    806
    807
                let result = transpiler.transpile(&ast).unwrap();
    808
                let rust_str = result.to_string();
    809
    810
                assert!(rust_str.contains("true") && rust_str.contains("false"),
    811
                       "Failed for operator {op}: {rust_str}");
    812
            }
    813
        }
    814
    815
        #[test]
    816
        fn test_transpile_unary_operators() {
    817
            let test_cases = vec![
    818
                ("!true", "true"),
    819
                ("-5", "5"),
    820
            ];
    821
    822
            for (code, expected) in test_cases {
    823
                let transpiler = create_transpiler();
    824
                let mut parser = Parser::new(code);
    825
                let ast = parser.parse().unwrap();
    826
    827
                let result = transpiler.transpile(&ast).unwrap();
    828
                let rust_str = result.to_string();
    829
    830
                assert!(rust_str.contains(expected),
    831
                       "Failed for {code}: {rust_str}");
    832
            }
    833
        }
    834
    835
        #[test]
    836
        fn test_transpile_identifier() {
    837
            let transpiler = create_transpiler();
    838
            let code = "variable_name";
    839
            let mut parser = Parser::new(code);
    840
            let ast = parser.parse().unwrap();
    841
    842
            let result = transpiler.transpile(&ast).unwrap();
    843
            let rust_str = result.to_string();
    844
    845
            assert!(rust_str.contains("variable_name"));
    846
        }
    847
    848
        #[test]
    849
        fn test_transpile_function_call() {
    850
            let transpiler = create_transpiler();
    851
            let code = "func_name(arg1, arg2)";
    852
            let mut parser = Parser::new(code);
    853
            let ast = parser.parse().unwrap();
    854
    855
            let result = transpiler.transpile(&ast).unwrap();
    856
            let rust_str = result.to_string();
    857
    858
            assert!(rust_str.contains("func_name"));
    859
            assert!(rust_str.contains("arg1"));
    860
            assert!(rust_str.contains("arg2"));
    861
        }
    862
    863
        #[test]
    864
        fn test_transpile_function_call_no_args() {
    865
            let transpiler = create_transpiler();
    866
            let code = "func_name()";
    867
            let mut parser = Parser::new(code);
    868
            let ast = parser.parse().unwrap();
    869
    870
            let result = transpiler.transpile(&ast).unwrap();
    871
            let rust_str = result.to_string();
    872
    873
            assert!(rust_str.contains("func_name"));
    874
            assert!(rust_str.contains("()"));
    875
        }
    876
    877
        #[test]
    878
        fn test_transpile_list() {
    879
            let transpiler = create_transpiler();
    880
            let code = "[1, 2, 3]";
    881
            let mut parser = Parser::new(code);
    882
            let ast = parser.parse().unwrap();
    883
    884
            let result = transpiler.transpile(&ast).unwrap();
    885
            let rust_str = result.to_string();
    886
    887
            assert!(rust_str.contains("vec!") || rust_str.contains("["));
    888
            assert!(rust_str.contains("1") && rust_str.contains("2") && rust_str.contains("3"));
    889
        }
    890
    891
        #[test]
    892
        fn test_transpile_empty_list() {
    893
            let transpiler = create_transpiler();
    894
            let code = "[]";
    895
            let mut parser = Parser::new(code);
    896
            let ast = parser.parse().unwrap();
    897
    898
            let result = transpiler.transpile(&ast).unwrap();
    899
            let rust_str = result.to_string();
    900
    901
            assert!(rust_str.contains("vec!") || rust_str.contains("[]"));
    902
        }
    903
    904
        #[test]
    905
        fn test_transpile_range() {
    906
            let transpiler = create_transpiler();
    907
            let code = "1..10";
    908
            let mut parser = Parser::new(code);
    909
            let ast = parser.parse().unwrap();
    910
    911
            let result = transpiler.transpile(&ast).unwrap();
    912
            let rust_str = result.to_string();
    913
    914
            assert!(rust_str.contains("1") && rust_str.contains("10"));
    915
            assert!(rust_str.contains("..") || rust_str.contains("range"));
    916
        }
    917
    918
        #[test]
    919
        fn test_transpile_inclusive_range() {
    920
            let transpiler = create_transpiler();
    921
            let code = "1..=10";
    922
            let mut parser = Parser::new(code);
    923
            let ast = parser.parse().unwrap();
    924
    925
            let result = transpiler.transpile(&ast).unwrap();
    926
            let rust_str = result.to_string();
    927
    928
            assert!(rust_str.contains("1") && rust_str.contains("10"));
    929
            assert!(rust_str.contains("..=") || rust_str.contains("inclusive"));
    930
        }
    931
    932
        #[test]
    933
        fn test_transpile_block_expression() {
    934
            let transpiler = create_transpiler();
    935
            let code = "{ let x = 5; x }";
    936
            let mut parser = Parser::new(code);
    937
            let ast = parser.parse().unwrap();
    938
    939
            let result = transpiler.transpile(&ast).unwrap();
    940
            let rust_str = result.to_string();
    941
    942
            assert!(rust_str.contains("{"));
    943
            assert!(rust_str.contains("}"));
    944
            assert!(rust_str.contains("let"));
    945
            assert!(rust_str.contains("x"));
    946
            assert!(rust_str.contains("5"));
    947
        }
    948
    949
        #[test]
    950
        fn test_definitely_string_detection() {
    951
            // String literals should be definitely strings
    952
            let code1 = "\"hello\"";
    953
            let mut parser1 = Parser::new(code1);
    954
            let ast1 = parser1.parse().unwrap();
    955
            if let ExprKind::Block(exprs) = &ast1.kind {
    956
                if let Some(expr) = exprs.first() {
    957
                    assert!(Transpiler::is_definitely_string(expr));
    958
                }
    959
            }
    960
    961
            // Numbers should not be definitely strings
    962
            let code2 = "42";
    963
            let mut parser2 = Parser::new(code2);
    964
            let ast2 = parser2.parse().unwrap();
    965
            if let ExprKind::Block(exprs) = &ast2.kind {
    966
                if let Some(expr) = exprs.first() {
    967
                    assert!(!Transpiler::is_definitely_string(expr));
    968
                }
    969
            }
    970
        }
    971
    972
        #[test]
    973
        fn test_complex_nested_expressions() {
    974
            let transpiler = create_transpiler();
    975
            let code = "(5 + 3) * (10 - 2)";
    976
            let mut parser = Parser::new(code);
    977
            let ast = parser.parse().unwrap();
    978
    979
            let result = transpiler.transpile(&ast).unwrap();
    980
            let rust_str = result.to_string();
    981
    982
            // Should handle nested arithmetic with parentheses
    983
            assert!(rust_str.contains("5") && rust_str.contains("3"));
    984
            assert!(rust_str.contains("10") && rust_str.contains("2"));
    985
            assert!(rust_str.contains("+") && rust_str.contains("-") && rust_str.contains("*"));
    986
        }
    987
    988
        #[test]
    989
        fn test_integer_literal_size_handling() {
    990
            // Small integers
    991
            assert_eq!(
    992
                Transpiler::transpile_integer(42).to_string(),
    993
                "42i32"
    994
            );
    995
    996
            // Large integers
    997
            #[allow(clippy::unreadable_literal)]
    998
            let large_int = 9223372036854775807;
    999
            assert_eq!(
    1000
                Transpiler::transpile_integer(large_int).to_string(),
    1001
                "9223372036854775807i64"
    1002
            );
    1003
    1004
            // Negative integers
    1005
            assert_eq!(
    1006
                Transpiler::transpile_integer(-42).to_string(),
    1007
                "- 42i32"
    1008
            );
    1009
        }
    1010
    1011
        #[test]
    1012
        fn test_method_call_transpilation() {
    1013
            let transpiler = create_transpiler();
    1014
            let code = "obj.method(arg)";
    1015
            let mut parser = Parser::new(code);
    1016
            let ast = parser.parse().unwrap();
    1017
    1018
            let result = transpiler.transpile(&ast).unwrap();
    1019
            let rust_str = result.to_string();
    1020
    1021
            assert!(rust_str.contains("obj"));
    1022
            assert!(rust_str.contains("method"));
    1023
            assert!(rust_str.contains("arg"));
    1024
        }
    1025
    1026
        #[test]
    1027
        fn test_string_interpolation_transpilation() {
    1028
            let transpiler = create_transpiler();
    1029
            let code = "f\"Hello {name}\"";
    1030
            let mut parser = Parser::new(code);
    1031
            let ast = parser.parse().unwrap();
    1032
    1033
            let result = transpiler.transpile(&ast).unwrap();
    1034
            let rust_str = result.to_string();
    1035
    1036
            // String interpolation should use format!
    1037
            assert!(rust_str.contains("format!") || rust_str.contains("Hello"));
    1038
            assert!(rust_str.contains("name"));
    1039
        }
    1040
    }