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

    Line
    Count
    Source
    1
    //! WebAssembly REPL implementation for browser-based evaluation
    2
    //!
    3
    //! Provides interactive Ruchy evaluation in the browser with progressive enhancement.
    4
    5
    use std::collections::HashMap;
    6
    7
    #[cfg(target_arch = "wasm32")]
    8
    use wasm_bindgen::prelude::*;
    9
    10
    #[cfg(not(target_arch = "wasm32"))]
    11
    use serde::{Serialize, Deserialize};
    12
    13
    // For non-WASM builds, provide stub types
    14
    #[cfg(not(target_arch = "wasm32"))]
    15
    #[derive(Debug, Clone)]
    16
    pub struct JsValue;
    17
    18
    // ============================================================================
    19
    // REPL Output Types
    20
    // ============================================================================
    21
    22
    #[derive(Debug, Clone)]
    23
    #[cfg_attr(not(target_arch = "wasm32"), derive(Serialize, Deserialize))]
    24
    pub struct ReplOutput {
    25
        pub success: bool,
    26
        pub display: Option<String>,
    27
        pub type_info: Option<String>,
    28
        pub rust_code: Option<String>,
    29
        pub error: Option<String>,
    30
        pub timing: TimingInfo,
    31
    }
    32
    33
    #[derive(Debug, Clone)]
    34
    #[cfg_attr(not(target_arch = "wasm32"), derive(Serialize, Deserialize))]
    35
    pub struct TimingInfo {
    36
        pub parse_ms: f64,
    37
        pub typecheck_ms: f64,
    38
        pub eval_ms: f64,
    39
        pub total_ms: f64,
    40
    }
    41
    42
    // ============================================================================
    43
    // WASM REPL Implementation
    44
    // ============================================================================
    45
    46
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    47
    pub struct WasmRepl {
    48
        /// Bindings for variables
    49
        bindings: HashMap<String, String>,
    50
        /// Command history
    51
        history: Vec<String>,
    52
        /// Session ID for tracking
    53
        session_id: String,
    54
    }
    55
    56
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    57
    impl WasmRepl {
    58
        /// Create a new WASM REPL instance
    59
        #[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
    60
    0
        pub fn new() -> Result<WasmRepl, JsValue> {
    61
            #[cfg(target_arch = "wasm32")]
    62
            console_error_panic_hook::set_once();
    63
    64
    0
            Ok(WasmRepl {
    65
    0
                bindings: HashMap::new(),
    66
    0
                history: Vec::new(),
    67
    0
                session_id: generate_session_id(),
    68
    0
            })
    69
    0
        }
    70
    71
        /// Evaluate a Ruchy expression
    72
        #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    73
    0
        pub fn eval(&mut self, input: &str) -> Result<String, JsValue> {
    74
    0
            let start = get_timestamp();
    75
    76
            // Parse the input
    77
    0
            let parse_start = get_timestamp();
    78
    0
            let mut parser = crate::Parser::new(input);
    79
    0
            let ast = match parser.parse() {
    80
    0
                Ok(ast) => ast,
    81
    0
                Err(e) => {
    82
    0
                    return Ok(serde_json::to_string(&ReplOutput {
    83
    0
                        success: false,
    84
    0
                        display: None,
    85
    0
                        type_info: None,
    86
    0
                        rust_code: None,
    87
    0
                        error: Some(format!("Parse error: {e}")),
    88
    0
                        timing: TimingInfo {
    89
    0
                            parse_ms: get_timestamp() - parse_start,
    90
    0
                            typecheck_ms: 0.0,
    91
    0
                            eval_ms: 0.0,
    92
    0
                            total_ms: get_timestamp() - start,
    93
    0
                        },
    94
    0
                    }).unwrap_or_else(|_| "Error serializing output".to_string()));
    95
                }
    96
            };
    97
    0
            let parse_time = get_timestamp() - parse_start;
    98
    99
            // Type checking would go here
    100
    0
            let typecheck_start = get_timestamp();
    101
            // ... type checking implementation ...
    102
    0
            let typecheck_time = get_timestamp() - typecheck_start;
    103
    104
            // Evaluation
    105
    0
            let eval_start = get_timestamp();
    106
            // For now, just return the parsed AST as a string
    107
    0
            let result = format!("{ast:?}");
    108
    0
            let eval_time = get_timestamp() - eval_start;
    109
    110
            // Add to history
    111
    0
            self.history.push(input.to_string());
    112
    113
            // Return result
    114
    0
            Ok(serde_json::to_string(&ReplOutput {
    115
    0
                success: true,
    116
    0
                display: Some(result),
    117
    0
                type_info: Some("Any".to_string()),
    118
    0
                rust_code: None,
    119
    0
                error: None,
    120
    0
                timing: TimingInfo {
    121
    0
                    parse_ms: parse_time,
    122
    0
                    typecheck_ms: typecheck_time,
    123
    0
                    eval_ms: eval_time,
    124
    0
                    total_ms: get_timestamp() - start,
    125
    0
                },
    126
    0
            }).unwrap_or_else(|_| "Error serializing output".to_string()))
    127
    0
        }
    128
    129
        /// Get command history
    130
        #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    131
    0
        pub fn get_history(&self) -> Vec<String> {
    132
    0
            self.history.clone()
    133
    0
        }
    134
    135
        /// Clear the REPL state
    136
        #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    137
    0
        pub fn clear(&mut self) {
    138
    0
            self.bindings.clear();
    139
    0
            self.history.clear();
    140
    0
        }
    141
    142
        /// Get session ID
    143
        #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    144
    0
        pub fn session_id(&self) -> String {
    145
    0
            self.session_id.clone()
    146
    0
        }
    147
    }
    148
    149
    impl Default for WasmRepl {
    150
    0
        fn default() -> Self {
    151
    0
            Self::new().unwrap()
    152
    0
        }
    153
    }
    154
    155
    // ============================================================================
    156
    // Helper Functions
    157
    // ============================================================================
    158
    159
    /// Generate a unique session ID
    160
    0
    fn generate_session_id() -> String {
    161
        #[cfg(target_arch = "wasm32")]
    162
        {
    163
            // Use browser's crypto API for UUID
    164
            format!("session-{}", js_sys::Date::now())
    165
        }
    166
        #[cfg(not(target_arch = "wasm32"))]
    167
        {
    168
            // Use system time for non-WASM builds
    169
    0
            format!("session-{}", std::time::SystemTime::now()
    170
    0
                .duration_since(std::time::UNIX_EPOCH)
    171
    0
                .unwrap()
    172
    0
                .as_millis())
    173
        }
    174
    0
    }
    175
    176
    /// Get current timestamp in milliseconds
    177
    0
    fn get_timestamp() -> f64 {
    178
        #[cfg(target_arch = "wasm32")]
    179
        {
    180
            js_sys::Date::now()
    181
        }
    182
        #[cfg(not(target_arch = "wasm32"))]
    183
        {
    184
    0
            std::time::SystemTime::now()
    185
    0
                .duration_since(std::time::UNIX_EPOCH)
    186
    0
                .unwrap()
    187
    0
                .as_millis() as f64
    188
        }
    189
    0
    }
    190
    191
    // ============================================================================
    192
    // Memory Management
    193
    // ============================================================================
    194
    195
    /// Heap allocator for WASM
    196
    pub struct WasmHeap {
    197
        /// Young generation for short-lived objects
    198
        young: Vec<u8>,
    199
        /// Old generation for long-lived objects
    200
        old: Vec<u8>,
    201
        /// GC roots
    202
        roots: Vec<usize>,
    203
    }
    204
    205
    impl WasmHeap {
    206
    0
        pub fn new() -> Self {
    207
    0
            Self {
    208
    0
                young: Vec::with_capacity(256 * 1024), // 256KB
    209
    0
                old: Vec::with_capacity(2 * 1024 * 1024), // 2MB
    210
    0
                roots: Vec::new(),
    211
    0
            }
    212
    0
        }
    213
    214
        /// Perform minor garbage collection
    215
    0
        pub fn minor_gc(&mut self) {
    216
    0
            self.young.clear();
    217
    0
        }
    218
    219
        /// Perform major garbage collection
    220
    0
        pub fn major_gc(&mut self) {
    221
            // Mark phase
    222
    0
            let mut marked = vec![false; self.old.len()];
    223
    0
            for &root in &self.roots {
    224
    0
                if root < marked.len() {
    225
    0
                    marked[root] = true;
    226
    0
                }
    227
            }
    228
    229
            // Compact phase (simplified)
    230
    0
            let mut compacted = Vec::new();
    231
    0
            for (i, &is_marked) in marked.iter().enumerate() {
    232
    0
                if is_marked && i < self.old.len() {
    233
    0
                    compacted.push(self.old[i]);
    234
    0
                }
    235
            }
    236
    0
            self.old = compacted;
    237
    0
        }
    238
    }
    239
    240
    impl Default for WasmHeap {
    241
    0
        fn default() -> Self {
    242
    0
            Self::new()
    243
    0
        }
    244
    }
    245
    246
    // ============================================================================
    247
    // Tests
    248
    // ============================================================================
    249
    250
    #[cfg(test)]
    251
    mod tests {
    252
        use super::*;
    253
    254
        #[test]
    255
        fn test_wasm_repl_creation() {
    256
            let repl = WasmRepl::new();
    257
            assert!(repl.is_ok());
    258
        }
    259
    260
        #[test]
    261
        fn test_session_id() {
    262
            let repl = WasmRepl::new().unwrap();
    263
            assert!(repl.session_id().starts_with("session-"));
    264
        }
    265
    266
        #[test]
    267
        fn test_heap() {
    268
            let mut heap = WasmHeap::new();
    269
            heap.minor_gc();
    270
            heap.major_gc();
    271
            assert!(heap.young.is_empty());
    272
        }
    273
    }