ruchy/
lib.rs

1//! Ruchy: A modern systems programming language
2//!
3//! Ruchy combines functional programming with systems programming capabilities,
4//! featuring an ML-style syntax, advanced type inference, and zero-cost abstractions.
5
6#![warn(clippy::all)]
7// Temporarily disabled pedantic for RUCHY-0801 - Re-enable in quality sprint
8// #![warn(clippy::pedantic)]
9#![allow(clippy::module_name_repetitions)]
10#![allow(clippy::must_use_candidate)]
11// Clippy allows for RUCHY-0801 commit - will be addressed in quality sprint
12#![allow(clippy::case_sensitive_file_extension_comparisons)]
13#![allow(clippy::match_same_arms)]
14#![allow(clippy::struct_excessive_bools)]
15#![allow(clippy::cast_precision_loss)]
16#![allow(clippy::cast_possible_truncation)]
17#![allow(clippy::unused_self)]
18#![allow(clippy::expect_used)]
19#![allow(clippy::missing_errors_doc)]
20#![allow(clippy::missing_panics_doc)]
21// Additional clippy allows for P0 lint fixes  
22#![allow(clippy::empty_line_after_doc_comments)]
23#![allow(clippy::manual_let_else)]
24#![allow(clippy::redundant_pattern_matching)]
25#![allow(clippy::items_after_statements)]
26#![allow(clippy::too_many_lines)]
27#![allow(clippy::type_complexity)]
28#![allow(dead_code)]
29#![allow(clippy::float_cmp)]
30#![allow(clippy::collapsible_match)]  
31#![allow(clippy::cast_sign_loss)]
32#![allow(clippy::manual_strip)]
33#![allow(clippy::implicit_hasher)]
34#![allow(clippy::too_many_arguments)]
35#![allow(clippy::trivially_copy_pass_by_ref)]
36#![allow(clippy::unnecessary_wraps)]
37#![allow(clippy::only_used_in_recursion)]
38#![allow(clippy::print_stdout)]
39#![allow(clippy::print_stderr)]
40#![allow(clippy::format_push_string)]
41#![allow(clippy::field_reassign_with_default)]
42#![allow(clippy::return_self_not_must_use)]
43#![allow(clippy::unwrap_used)]
44#![allow(clippy::needless_pass_by_value)]
45#![allow(clippy::manual_clamp)]
46#![allow(clippy::should_implement_trait)]
47#![allow(clippy::unnecessary_to_owned)]
48#![allow(clippy::cast_possible_wrap)]
49#![allow(clippy::if_same_then_else)]
50
51#[cfg(feature = "mcp")]
52pub mod actors;
53pub mod backend;
54pub mod frontend;
55pub mod lints;
56#[cfg(feature = "mcp")]
57pub mod lsp;
58#[cfg(feature = "mcp")]
59pub mod mcp;
60pub mod middleend;
61pub mod parser;
62pub mod proving;
63pub mod quality;
64pub mod runtime;
65#[cfg(any(test, feature = "testing"))]
66pub mod testing;
67#[cfg(any(test, feature = "testing"))]
68pub use testing::AstBuilder;
69pub mod transpiler;
70pub mod wasm;
71
72#[cfg(feature = "mcp")]
73pub use actors::{
74    Actor, ActorHandle, McpActor, McpMessage, McpResponse, SupervisionStrategy, Supervisor,
75};
76pub use backend::{ModuleResolver, Transpiler};
77pub use frontend::ast::{BinaryOp, Expr, ExprKind, Literal, Pattern, UnaryOp};
78pub use frontend::lexer::{Token, TokenStream};
79pub use frontend::parser::Parser;
80#[cfg(feature = "mcp")]
81pub use lsp::{start_server, start_tcp_server, Formatter, RuchyLanguageServer, SemanticAnalyzer};
82pub use quality::{
83    CiQualityEnforcer, CoverageCollector, CoverageReport, CoverageTool, FileCoverage,
84    HtmlReportGenerator, QualityGates, QualityMetrics, QualityReport, QualityThresholds,
85};
86pub use quality::gates::{QualityGateEnforcer, QualityGateConfig, GateResult};
87
88use anyhow::Result;
89
90/// Compile Ruchy source code to Rust
91///
92/// # Examples
93///
94/// ```
95/// use ruchy::compile;
96///
97/// let rust_code = compile("42").expect("Failed to compile");
98/// assert!(rust_code.contains("42"));
99/// ```
100///
101/// # Errors
102///
103/// Returns an error if:
104/// - The source code cannot be parsed
105/// - The transpilation to Rust fails
106pub fn compile(source: &str) -> Result<String> {
107    let mut parser = Parser::new(source);
108    let ast = parser.parse()?;
109    let transpiler = Transpiler::new();
110    // Use transpile_to_program to wrap in main() for standalone compilation
111    let rust_code = transpiler.transpile_to_program(&ast)?;
112    Ok(rust_code.to_string())
113}
114
115/// Check if the given source code has valid syntax
116#[must_use]
117pub fn is_valid_syntax(source: &str) -> bool {
118    let mut parser = Parser::new(source);
119    parser.parse().is_ok()
120}
121
122/// Get parse error details if the source has syntax errors
123#[must_use]
124pub fn get_parse_error(source: &str) -> Option<String> {
125    let mut parser = Parser::new(source);
126    parser.parse().err().map(|e| e.to_string())
127}
128
129/// Run the REPL
130///
131/// # Examples
132///
133/// ```no_run
134/// use ruchy::run_repl;
135///
136/// run_repl().expect("Failed to run REPL");
137/// ```
138///
139/// # Errors
140///
141/// Returns an error if:
142/// - The REPL cannot be initialized
143/// - User interaction fails
144pub fn run_repl() -> Result<()> {
145    let mut repl = runtime::repl::Repl::new()?;
146    repl.run()
147}
148
149#[cfg(test)]
150mod test_config {
151    use std::sync::Once;
152
153    static INIT: Once = Once::new();
154
155    /// Initialize test configuration once per test run
156    pub fn init() {
157        INIT.call_once(|| {
158            // Limit proptest for development (CI uses different settings)
159            if std::env::var("CI").is_err() {
160                std::env::set_var("PROPTEST_CASES", "10");
161                std::env::set_var("PROPTEST_MAX_SHRINK_ITERS", "50");
162            }
163            // Limit test threads if not already set
164            if std::env::var("RUST_TEST_THREADS").is_err() {
165                std::env::set_var("RUST_TEST_THREADS", "4");
166            }
167        });
168    }
169}
170
171#[cfg(test)]
172#[allow(clippy::unwrap_used)]
173#[allow(clippy::single_char_pattern)]
174mod tests {
175    use super::test_config;
176    use super::*;
177
178    #[test]
179    fn test_compile_simple() {
180        test_config::init();
181        let result = compile("42").unwrap();
182        assert!(result.contains("42"));
183    }
184
185    #[test]
186    fn test_compile_let() {
187        let result = compile("let x = 10 in x + 1").unwrap();
188        assert!(result.contains("let"));
189        assert!(result.contains("10"));
190    }
191
192    #[test]
193    fn test_compile_function() {
194        let result = compile("fun add(x: i32, y: i32) -> i32 { x + y }").unwrap();
195        assert!(result.contains("fn"));
196        assert!(result.contains("add"));
197        assert!(result.contains("i32"));
198    }
199
200    #[test]
201    fn test_compile_if() {
202        let result = compile("if true { 1 } else { 0 }").unwrap();
203        assert!(result.contains("if"));
204        assert!(result.contains("else"));
205    }
206
207    #[test]
208    fn test_compile_match() {
209        let result = compile("match x { 0 => \"zero\", _ => \"other\" }").unwrap();
210        assert!(result.contains("match"));
211    }
212
213    #[test]
214    fn test_compile_list() {
215        let result = compile("[1, 2, 3]").unwrap();
216        assert!(result.contains("vec") && result.contains("!"));
217    }
218
219    #[test]
220    fn test_compile_lambda() {
221        let result = compile("|x| x * 2").unwrap();
222        assert!(result.contains("|"));
223    }
224
225    #[test]
226    fn test_compile_struct() {
227        let result = compile("struct Point { x: f64, y: f64 }").unwrap();
228        assert!(result.contains("struct"));
229        assert!(result.contains("Point"));
230    }
231
232    #[test]
233    fn test_compile_impl() {
234        let result =
235            compile("impl Point { fun new() -> Point { Point { x: 0.0, y: 0.0 } } }").unwrap();
236        assert!(result.contains("impl"));
237    }
238
239    #[test]
240    fn test_compile_trait() {
241        let result = compile("trait Show { fun show(&self) -> String }").unwrap();
242        assert!(result.contains("trait"));
243    }
244
245    #[test]
246    fn test_compile_for_loop() {
247        let result = compile("for x in [1, 2, 3] { print(x) }").unwrap();
248        assert!(result.contains("for"));
249    }
250
251    #[test]
252    fn test_compile_binary_ops() {
253        let result = compile("1 + 2 * 3 - 4 / 2").unwrap();
254        assert!(result.contains("+"));
255        assert!(result.contains("*"));
256        assert!(result.contains("-"));
257        assert!(result.contains("/"));
258    }
259
260    #[test]
261    fn test_compile_comparison_ops() {
262        let result = compile("x < y && y <= z").unwrap();
263        assert!(result.contains("<"));
264        assert!(result.contains("<="));
265        assert!(result.contains("&&"));
266    }
267
268    #[test]
269    fn test_compile_unary_ops() {
270        let result = compile("-x").unwrap();
271        assert!(result.contains("-"));
272
273        let result = compile("!flag").unwrap();
274        assert!(result.contains("!"));
275    }
276
277    #[test]
278    fn test_compile_call() {
279        let result = compile("func(1, 2, 3)").unwrap();
280        assert!(result.contains("func"));
281        assert!(result.contains("("));
282        assert!(result.contains(")"));
283    }
284
285    #[test]
286    fn test_compile_method_call() {
287        let result = compile("obj.method()").unwrap();
288        assert!(result.contains("."));
289        assert!(result.contains("method"));
290    }
291
292    #[test]
293    fn test_compile_block() {
294        let result = compile("{ let x = 1; x + 1 }").unwrap();
295        assert!(result.contains("{"));
296        assert!(result.contains("}"));
297    }
298
299    #[test]
300    fn test_compile_string() {
301        let result = compile("\"hello world\"").unwrap();
302        assert!(result.contains("hello world"));
303    }
304
305    #[test]
306    fn test_compile_bool() {
307        let result = compile("true && false").unwrap();
308        assert!(result.contains("true"));
309        assert!(result.contains("false"));
310    }
311
312    #[test]
313    fn test_compile_unit() {
314        let result = compile("()").unwrap();
315        assert!(result.contains("()"));
316    }
317
318    #[test]
319    fn test_compile_nested_let() {
320        let result = compile("let x = 1 in let y = 2 in x + y").unwrap();
321        assert!(result.contains("let"));
322    }
323
324    #[test]
325    fn test_compile_nested_if() {
326        let result = compile("if x { if y { 1 } else { 2 } } else { 3 }").unwrap();
327        assert!(result.contains("if"));
328    }
329
330    #[test]
331    fn test_compile_empty_list() {
332        let result = compile("[]").unwrap();
333        assert!(result.contains("vec") && result.contains("!"));
334    }
335
336    #[test]
337    fn test_compile_empty_block() {
338        let result = compile("{ }").unwrap();
339        assert!(result.contains("()"));
340    }
341
342    #[test]
343    fn test_compile_float() {
344        let result = compile("3.14159").unwrap();
345        assert!(result.contains("3.14159"));
346    }
347
348    #[test]
349    fn test_compile_large_int() {
350        let result = compile("999999999").unwrap();
351        assert!(result.contains("999999999"));
352    }
353
354    #[test]
355    fn test_compile_string_escape() {
356        let result = compile(r#""hello\nworld""#).unwrap();
357        assert!(result.contains("hello"));
358    }
359
360    #[test]
361    fn test_compile_power_op() {
362        let result = compile("2 ** 8").unwrap();
363        assert!(result.contains("pow"));
364    }
365
366    #[test]
367    fn test_compile_modulo() {
368        let result = compile("10 % 3").unwrap();
369        assert!(result.contains("%"));
370    }
371
372    #[test]
373    fn test_compile_bitwise_ops() {
374        let result = compile("a & b | c ^ d").unwrap();
375        assert!(result.contains("&"));
376        assert!(result.contains("|"));
377        assert!(result.contains("^"));
378    }
379
380    #[test]
381    fn test_compile_left_shift() {
382        let result = compile("x << 2").unwrap();
383        assert!(result.contains("<<"));
384    }
385
386    #[test]
387    fn test_compile_not_equal() {
388        let result = compile("x != y").unwrap();
389        assert!(result.contains("!="));
390    }
391
392    #[test]
393    fn test_compile_greater_ops() {
394        let result = compile("x > y && x >= z").unwrap();
395        assert!(result.contains(">"));
396        assert!(result.contains(">="));
397    }
398
399    #[test]
400    fn test_compile_or_op() {
401        let result = compile("x || y").unwrap();
402        assert!(result.contains("||"));
403    }
404
405    #[test]
406    fn test_compile_complex_expression() {
407        let result = compile("(x + y) * (z - w) / 2").unwrap();
408        assert!(result.contains("+"));
409        assert!(result.contains("-"));
410        assert!(result.contains("*"));
411        assert!(result.contains("/"));
412    }
413
414    #[test]
415    fn test_compile_errors() {
416        assert!(compile("").is_err());
417        assert!(compile("   ").is_err());
418        assert!(compile("let x =").is_err());
419        assert!(compile("if").is_err());
420        assert!(compile("match").is_err());
421    }
422
423    #[test]
424    fn test_is_valid_syntax_valid_cases() {
425        assert!(is_valid_syntax("42"));
426        assert!(is_valid_syntax("3.14"));
427        assert!(is_valid_syntax("true"));
428        assert!(is_valid_syntax("false"));
429        assert!(is_valid_syntax("\"hello\""));
430        assert!(is_valid_syntax("x + y"));
431        assert!(is_valid_syntax("[1, 2, 3]"));
432        assert!(is_valid_syntax("if true { 1 } else { 2 }"));
433    }
434
435    #[test]
436    fn test_is_valid_syntax_invalid_cases() {
437        assert!(!is_valid_syntax(""));
438        assert!(!is_valid_syntax("   "));
439        assert!(!is_valid_syntax("let x ="));
440        assert!(!is_valid_syntax("if { }"));
441        assert!(!is_valid_syntax("[1, 2,"));
442        assert!(!is_valid_syntax("match"));
443        assert!(!is_valid_syntax("struct"));
444    }
445
446    #[test]
447    fn test_get_parse_error_with_errors() {
448        let error = get_parse_error("fun (");
449        assert!(error.is_some());
450        // Error message format may vary, just check that we got an error
451        assert!(!error.unwrap().is_empty());
452    }
453
454    #[test]
455    fn test_get_parse_error_without_errors() {
456        let error = get_parse_error("42");
457        assert!(error.is_none());
458    }
459
460    #[test]
461    fn test_get_parse_error_detailed() {
462        let error = get_parse_error("if");
463        assert!(error.is_some());
464
465        let error = get_parse_error("match");
466        assert!(error.is_some());
467
468        let error = get_parse_error("[1, 2,");
469        assert!(error.is_some());
470    }
471
472    #[test]
473    fn test_compile_generic_function() {
474        let result = compile("fun id<T>(x: T) -> T { x }").unwrap();
475        assert!(result.contains("fn"));
476        assert!(result.contains("id"));
477    }
478
479    #[test]
480    fn test_compile_generic_struct() {
481        let result = compile("struct Box<T> { value: T }").unwrap();
482        assert!(result.contains("struct"));
483        assert!(result.contains("Box"));
484    }
485
486    #[test]
487    fn test_compile_multiple_statements() {
488        let result = compile("let x = 1 in let y = 2 in x + y").unwrap();
489        assert!(result.contains("let"));
490    }
491
492    #[test]
493    fn test_compile_pattern_matching() {
494        let result = compile("match x { 0 => \"zero\", _ => \"other\" }").unwrap();
495        assert!(result.contains("match"));
496    }
497
498    #[test]
499    fn test_compile_struct_literal() {
500        let result = compile("Point { x: 10, y: 20 }").unwrap();
501        assert!(result.contains("Point"));
502    }
503
504    // Test removed - try/catch operations removed in RUCHY-0834
505    // #[test]
506    // fn test_compile_try_operator() {
507    //     let result = compile("func()?").unwrap();
508    //     assert!(result.contains("?"));
509    // }
510
511    #[test]
512    fn test_compile_await_expression() {
513        let result = compile("async_func().await").unwrap();
514        assert!(result.contains("await"));
515    }
516
517    #[test]
518    fn test_compile_import() {
519        let result = compile("import std.collections.HashMap").unwrap();
520        assert!(result.contains("use"));
521    }
522
523    #[test]
524    fn test_compile_while_loop() {
525        let result = compile("while x < 10 { x + 1 }").unwrap();
526        assert!(result.contains("while"));
527    }
528
529    #[test]
530    fn test_compile_range() {
531        let result = compile("1..10").unwrap();
532        assert!(result.contains(".."));
533    }
534
535    #[test]
536    fn test_compile_pipeline() {
537        let result = compile("data |> filter |> map").unwrap();
538        assert!(result.contains("("));
539    }
540
541    #[test]
542    fn test_compile_send_operation() {
543        let result = compile("myactor <- message").unwrap();
544        assert!(result.contains(". send (")); // Formatted with spaces
545        assert!(result.contains(". await")); // Formatted with spaces
546    }
547
548    #[test]
549    fn test_compile_ask_operation() {
550        let result = compile("myactor <? request").unwrap();
551        assert!(result.contains(". ask (")); // Formatted with spaces
552        assert!(result.contains(". await")); // Formatted with spaces
553    }
554
555    #[test]
556    fn test_compile_list_comprehension() {
557        let result = compile("[x * 2 for x in range(10)]").unwrap();
558        assert!(result.contains("map"));
559    }
560
561    #[test]
562    fn test_compile_actor() {
563        let result = compile(
564            r"
565            actor Counter {
566                count: i32,
567                
568                receive {
569                    Inc => 1,
570                    Get => 0
571                }
572            }
573        ",
574        )
575        .unwrap();
576        assert!(result.contains("struct Counter"));
577        assert!(result.contains("enum CounterMessage"));
578    }
579}