1#![warn(clippy::all)]
7#![allow(clippy::module_name_repetitions)]
10#![allow(clippy::must_use_candidate)]
11#![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::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
90pub fn compile(source: &str) -> Result<String> {
107 let mut parser = Parser::new(source);
108 let ast = parser.parse()?;
109 let mut transpiler = Transpiler::new();
110 let rust_code = transpiler.transpile_to_program(&ast)?;
112 Ok(rust_code.to_string())
113}
114
115#[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#[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
129pub 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 pub fn init() {
157 INIT.call_once(|| {
158 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 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 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]
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 (")); assert!(result.contains(". await")); }
547
548 #[test]
549 fn test_compile_ask_operation() {
550 let result = compile("myactor <? request").unwrap();
551 assert!(result.contains(". ask (")); assert!(result.contains(". await")); }
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}