Skip to main content

ruchy_wasm/
lib.rs

1//! WebAssembly bindings for Ruchy
2//!
3//! This crate provides WASM bindings for the Ruchy compiler, enabling:
4//! - Browser-based Ruchy compilation
5//! - Interactive code playgrounds
6//! - Educational tools and documentation
7//! - Real-time syntax validation
8//!
9//! # Example
10//!
11//! ```javascript
12//! import init, { RuchyCompiler } from './ruchy_wasm.js';
13//!
14//! async function compile() {
15//!     await init();
16//!     const compiler = new RuchyCompiler();
17//!     const rustCode = compiler.compile('fn add(a, b) { a + b }');
18//!     console.log(rustCode);
19//! }
20//! ```
21
22use ruchy::backend::transpiler::Transpiler;
23use ruchy::frontend::parser::Parser;
24use wasm_bindgen::prelude::*;
25
26/// WebAssembly compiler interface for Ruchy
27#[wasm_bindgen]
28pub struct RuchyCompiler {
29    transpiler: Transpiler,
30}
31
32#[wasm_bindgen]
33impl RuchyCompiler {
34    /// Create a new Ruchy compiler instance
35    #[wasm_bindgen(constructor)]
36    pub fn new() -> Self {
37        // Set panic hook for better browser debugging
38        console_error_panic_hook::set_once();
39
40        Self {
41            transpiler: Transpiler::new(),
42        }
43    }
44
45    /// Compile Ruchy code to Rust
46    ///
47    /// # Arguments
48    ///
49    /// * `source` - Ruchy source code as a string
50    ///
51    /// # Returns
52    ///
53    /// Transpiled Rust code as a string, or error message
54    #[wasm_bindgen]
55    pub fn compile(&mut self, source: &str) -> Result<String, JsValue> {
56        let mut parser = Parser::new(source);
57        let ast = parser
58            .parse()
59            .map_err(|e| JsValue::from_str(&format!("Parse error: {}", e)))?;
60
61        let rust_code = self
62            .transpiler
63            .transpile(&ast)
64            .map_err(|e| JsValue::from_str(&format!("Transpile error: {}", e)))?;
65
66        Ok(rust_code.to_string())
67    }
68
69    /// Validate Ruchy syntax without compilation
70    ///
71    /// # Arguments
72    ///
73    /// * `source` - Ruchy source code to validate
74    ///
75    /// # Returns
76    ///
77    /// `true` if syntax is valid, `false` otherwise
78    #[wasm_bindgen]
79    pub fn validate(&self, source: &str) -> bool {
80        Parser::new(source).parse().is_ok()
81    }
82
83    /// Get Ruchy compiler version
84    #[wasm_bindgen(getter)]
85    pub fn version(&self) -> String {
86        env!("CARGO_PKG_VERSION").to_string()
87    }
88
89    /// Parse Ruchy code and return AST as JSON
90    ///
91    /// # Arguments
92    ///
93    /// * `source` - Ruchy source code to parse
94    ///
95    /// # Returns
96    ///
97    /// AST representation as JSON string
98    #[wasm_bindgen]
99    pub fn parse_to_json(&self, source: &str) -> Result<String, JsValue> {
100        let mut parser = Parser::new(source);
101        let ast = parser
102            .parse()
103            .map_err(|e| JsValue::from_str(&format!("Parse error: {}", e)))?;
104
105        serde_json::to_string_pretty(&ast)
106            .map_err(|e| JsValue::from_str(&format!("JSON serialization error: {}", e)))
107    }
108}
109
110impl Default for RuchyCompiler {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use wasm_bindgen_test::*;
120
121    #[wasm_bindgen_test]
122    fn test_compile_simple_function() {
123        let mut compiler = RuchyCompiler::new();
124        let result = compiler.compile("fn add(a, b) { a + b }");
125        assert!(result.is_ok());
126    }
127
128    #[wasm_bindgen_test]
129    fn test_validate_valid_syntax() {
130        let compiler = RuchyCompiler::new();
131        assert!(compiler.validate("let x = 42"));
132    }
133
134    #[wasm_bindgen_test]
135    fn test_validate_invalid_syntax() {
136        let compiler = RuchyCompiler::new();
137        assert!(!compiler.validate("let x = "));
138    }
139
140    #[wasm_bindgen_test]
141    fn test_version() {
142        let compiler = RuchyCompiler::new();
143        assert!(!compiler.version().is_empty());
144    }
145}