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