/home/noah/src/ruchy/src/lib.rs
Line | Count | Source |
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")] |
52 | | pub mod actors; |
53 | | pub mod backend; |
54 | | pub mod frontend; |
55 | | pub mod lints; |
56 | | #[cfg(feature = "mcp")] |
57 | | pub mod lsp; |
58 | | #[cfg(feature = "mcp")] |
59 | | pub mod mcp; |
60 | | pub mod middleend; |
61 | | pub mod parser; |
62 | | pub mod proving; |
63 | | pub mod quality; |
64 | | pub mod runtime; |
65 | | #[cfg(any(test, feature = "testing"))] |
66 | | pub mod testing; |
67 | | #[cfg(any(test, feature = "testing"))] |
68 | | pub use testing::AstBuilder; |
69 | | pub mod transpiler; |
70 | | pub mod wasm; |
71 | | |
72 | | #[cfg(feature = "mcp")] |
73 | | pub use actors::{ |
74 | | Actor, ActorHandle, McpActor, McpMessage, McpResponse, SupervisionStrategy, Supervisor, |
75 | | }; |
76 | | pub use backend::{ModuleResolver, Transpiler}; |
77 | | pub use frontend::ast::{BinaryOp, Expr, ExprKind, Literal, Pattern, UnaryOp}; |
78 | | pub use frontend::lexer::{Token, TokenStream}; |
79 | | pub use frontend::parser::Parser; |
80 | | #[cfg(feature = "mcp")] |
81 | | pub use lsp::{start_server, start_tcp_server, Formatter, RuchyLanguageServer, SemanticAnalyzer}; |
82 | | pub use quality::{ |
83 | | CiQualityEnforcer, CoverageCollector, CoverageReport, CoverageTool, FileCoverage, |
84 | | HtmlReportGenerator, QualityGates, QualityMetrics, QualityReport, QualityThresholds, |
85 | | }; |
86 | | pub use quality::gates::{QualityGateEnforcer, QualityGateConfig, GateResult}; |
87 | | |
88 | | use 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 |
106 | 101 | pub fn compile(source: &str) -> Result<String> { |
107 | 101 | let mut parser = Parser::new(source); |
108 | 101 | let ast96 = parser.parse()?5 ; |
109 | 96 | let mut transpiler = Transpiler::new(); |
110 | | // Use transpile_to_program to wrap in main() for standalone compilation |
111 | 96 | let rust_code = transpiler.transpile_to_program(&ast)?0 ; |
112 | 96 | Ok(rust_code.to_string()) |
113 | 101 | } |
114 | | |
115 | | /// Check if the given source code has valid syntax |
116 | | #[must_use] |
117 | 18 | pub fn is_valid_syntax(source: &str) -> bool { |
118 | 18 | let mut parser = Parser::new(source); |
119 | 18 | parser.parse().is_ok() |
120 | 18 | } |
121 | | |
122 | | /// Get parse error details if the source has syntax errors |
123 | | #[must_use] |
124 | 5 | pub fn get_parse_error(source: &str) -> Option<String> { |
125 | 5 | let mut parser = Parser::new(source); |
126 | 5 | parser.parse().err().map(|e| e4 .to_string4 ()) |
127 | 5 | } |
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 |
144 | 0 | pub fn run_repl() -> Result<()> { |
145 | 0 | let mut repl = runtime::repl::Repl::new()?; |
146 | 0 | repl.run() |
147 | 0 | } |
148 | | |
149 | | #[cfg(test)] |
150 | | mod test_config { |
151 | | use std::sync::Once; |
152 | | |
153 | | static INIT: Once = Once::new(); |
154 | | |
155 | | /// Initialize test configuration once per test run |
156 | 1 | pub fn init() { |
157 | 1 | INIT.call_once(|| { |
158 | | // Limit proptest for development (CI uses different settings) |
159 | 1 | if std::env::var("CI").is_err() { |
160 | 1 | std::env::set_var("PROPTEST_CASES", "10"); |
161 | 1 | std::env::set_var("PROPTEST_MAX_SHRINK_ITERS", "50"); |
162 | 1 | }0 |
163 | | // Limit test threads if not already set |
164 | 1 | if std::env::var("RUST_TEST_THREADS").is_err() { |
165 | 0 | std::env::set_var("RUST_TEST_THREADS", "4"); |
166 | 1 | } |
167 | 1 | }); |
168 | 1 | } |
169 | | } |
170 | | |
171 | | #[cfg(test)] |
172 | | #[allow(clippy::unwrap_used)] |
173 | | #[allow(clippy::single_char_pattern)] |
174 | | mod tests { |
175 | | use super::test_config; |
176 | | use super::*; |
177 | | |
178 | | #[test] |
179 | 1 | fn test_compile_simple() { |
180 | 1 | test_config::init(); |
181 | 1 | let result = compile("42").unwrap(); |
182 | 1 | assert!(result.contains("42")); |
183 | 1 | } |
184 | | |
185 | | #[test] |
186 | 1 | fn test_compile_let() { |
187 | 1 | let result = compile("let x = 10 in x + 1").unwrap(); |
188 | 1 | assert!(result.contains("let")); |
189 | 1 | assert!(result.contains("10")); |
190 | 1 | } |
191 | | |
192 | | #[test] |
193 | 1 | fn test_compile_function() { |
194 | 1 | let result = compile("fun add(x: i32, y: i32) -> i32 { x + y }").unwrap(); |
195 | 1 | assert!(result.contains("fn")); |
196 | 1 | assert!(result.contains("add")); |
197 | 1 | assert!(result.contains("i32")); |
198 | 1 | } |
199 | | |
200 | | #[test] |
201 | 1 | fn test_compile_if() { |
202 | 1 | let result = compile("if true { 1 } else { 0 }").unwrap(); |
203 | 1 | assert!(result.contains("if")); |
204 | 1 | assert!(result.contains("else")); |
205 | 1 | } |
206 | | |
207 | | #[test] |
208 | 1 | fn test_compile_match() { |
209 | 1 | let result = compile("match x { 0 => \"zero\", _ => \"other\" }").unwrap(); |
210 | 1 | assert!(result.contains("match")); |
211 | 1 | } |
212 | | |
213 | | #[test] |
214 | 1 | fn test_compile_list() { |
215 | 1 | let result = compile("[1, 2, 3]").unwrap(); |
216 | 1 | assert!(result.contains("vec") && result.contains("!")); |
217 | 1 | } |
218 | | |
219 | | #[test] |
220 | 1 | fn test_compile_lambda() { |
221 | 1 | let result = compile("|x| x * 2").unwrap(); |
222 | 1 | assert!(result.contains("|")); |
223 | 1 | } |
224 | | |
225 | | #[test] |
226 | 1 | fn test_compile_struct() { |
227 | 1 | let result = compile("struct Point { x: f64, y: f64 }").unwrap(); |
228 | 1 | assert!(result.contains("struct")); |
229 | 1 | assert!(result.contains("Point")); |
230 | 1 | } |
231 | | |
232 | | #[test] |
233 | 1 | fn test_compile_impl() { |
234 | 1 | let result = |
235 | 1 | compile("impl Point { fun new() -> Point { Point { x: 0.0, y: 0.0 } } }").unwrap(); |
236 | 1 | assert!(result.contains("impl")); |
237 | 1 | } |
238 | | |
239 | | #[test] |
240 | 1 | fn test_compile_trait() { |
241 | 1 | let result = compile("trait Show { fun show(&self) -> String }").unwrap(); |
242 | 1 | assert!(result.contains("trait")); |
243 | 1 | } |
244 | | |
245 | | #[test] |
246 | 1 | fn test_compile_for_loop() { |
247 | 1 | let result = compile("for x in [1, 2, 3] { print(x) }").unwrap(); |
248 | 1 | assert!(result.contains("for")); |
249 | 1 | } |
250 | | |
251 | | #[test] |
252 | 1 | fn test_compile_binary_ops() { |
253 | 1 | let result = compile("1 + 2 * 3 - 4 / 2").unwrap(); |
254 | 1 | assert!(result.contains("+")); |
255 | 1 | assert!(result.contains("*")); |
256 | 1 | assert!(result.contains("-")); |
257 | 1 | assert!(result.contains("/")); |
258 | 1 | } |
259 | | |
260 | | #[test] |
261 | 1 | fn test_compile_comparison_ops() { |
262 | 1 | let result = compile("x < y && y <= z").unwrap(); |
263 | 1 | assert!(result.contains("<")); |
264 | 1 | assert!(result.contains("<=")); |
265 | 1 | assert!(result.contains("&&")); |
266 | 1 | } |
267 | | |
268 | | #[test] |
269 | 1 | fn test_compile_unary_ops() { |
270 | 1 | let result = compile("-x").unwrap(); |
271 | 1 | assert!(result.contains("-")); |
272 | | |
273 | 1 | let result = compile("!flag").unwrap(); |
274 | 1 | assert!(result.contains("!")); |
275 | 1 | } |
276 | | |
277 | | #[test] |
278 | 1 | fn test_compile_call() { |
279 | 1 | let result = compile("func(1, 2, 3)").unwrap(); |
280 | 1 | assert!(result.contains("func")); |
281 | 1 | assert!(result.contains("(")); |
282 | 1 | assert!(result.contains(")")); |
283 | 1 | } |
284 | | |
285 | | #[test] |
286 | 1 | fn test_compile_method_call() { |
287 | 1 | let result = compile("obj.method()").unwrap(); |
288 | 1 | assert!(result.contains(".")); |
289 | 1 | assert!(result.contains("method")); |
290 | 1 | } |
291 | | |
292 | | #[test] |
293 | 1 | fn test_compile_block() { |
294 | 1 | let result = compile("{ let x = 1; x + 1 }").unwrap(); |
295 | 1 | assert!(result.contains("{")); |
296 | 1 | assert!(result.contains("}")); |
297 | 1 | } |
298 | | |
299 | | #[test] |
300 | 1 | fn test_compile_string() { |
301 | 1 | let result = compile("\"hello world\"").unwrap(); |
302 | 1 | assert!(result.contains("hello world")); |
303 | 1 | } |
304 | | |
305 | | #[test] |
306 | 1 | fn test_compile_bool() { |
307 | 1 | let result = compile("true && false").unwrap(); |
308 | 1 | assert!(result.contains("true")); |
309 | 1 | assert!(result.contains("false")); |
310 | 1 | } |
311 | | |
312 | | #[test] |
313 | 1 | fn test_compile_unit() { |
314 | 1 | let result = compile("()").unwrap(); |
315 | 1 | assert!(result.contains("()")); |
316 | 1 | } |
317 | | |
318 | | #[test] |
319 | 1 | fn test_compile_nested_let() { |
320 | 1 | let result = compile("let x = 1 in let y = 2 in x + y").unwrap(); |
321 | 1 | assert!(result.contains("let")); |
322 | 1 | } |
323 | | |
324 | | #[test] |
325 | 1 | fn test_compile_nested_if() { |
326 | 1 | let result = compile("if x { if y { 1 } else { 2 } } else { 3 }").unwrap(); |
327 | 1 | assert!(result.contains("if")); |
328 | 1 | } |
329 | | |
330 | | #[test] |
331 | 1 | fn test_compile_empty_list() { |
332 | 1 | let result = compile("[]").unwrap(); |
333 | 1 | assert!(result.contains("vec") && result.contains("!")); |
334 | 1 | } |
335 | | |
336 | | #[test] |
337 | 1 | fn test_compile_empty_block() { |
338 | 1 | let result = compile("{ }").unwrap(); |
339 | 1 | assert!(result.contains("()")); |
340 | 1 | } |
341 | | |
342 | | #[test] |
343 | 1 | fn test_compile_float() { |
344 | 1 | let result = compile("3.14159").unwrap(); |
345 | 1 | assert!(result.contains("3.14159")); |
346 | 1 | } |
347 | | |
348 | | #[test] |
349 | 1 | fn test_compile_large_int() { |
350 | 1 | let result = compile("999999999").unwrap(); |
351 | 1 | assert!(result.contains("999999999")); |
352 | 1 | } |
353 | | |
354 | | #[test] |
355 | 1 | fn test_compile_string_escape() { |
356 | 1 | let result = compile(r#""hello\nworld""#).unwrap(); |
357 | 1 | assert!(result.contains("hello")); |
358 | 1 | } |
359 | | |
360 | | #[test] |
361 | 1 | fn test_compile_power_op() { |
362 | 1 | let result = compile("2 ** 8").unwrap(); |
363 | 1 | assert!(result.contains("pow")); |
364 | 1 | } |
365 | | |
366 | | #[test] |
367 | 1 | fn test_compile_modulo() { |
368 | 1 | let result = compile("10 % 3").unwrap(); |
369 | 1 | assert!(result.contains("%")); |
370 | 1 | } |
371 | | |
372 | | #[test] |
373 | 1 | fn test_compile_bitwise_ops() { |
374 | 1 | let result = compile("a & b | c ^ d").unwrap(); |
375 | 1 | assert!(result.contains("&")); |
376 | 1 | assert!(result.contains("|")); |
377 | 1 | assert!(result.contains("^")); |
378 | 1 | } |
379 | | |
380 | | #[test] |
381 | 1 | fn test_compile_left_shift() { |
382 | 1 | let result = compile("x << 2").unwrap(); |
383 | 1 | assert!(result.contains("<<")); |
384 | 1 | } |
385 | | |
386 | | #[test] |
387 | 1 | fn test_compile_not_equal() { |
388 | 1 | let result = compile("x != y").unwrap(); |
389 | 1 | assert!(result.contains("!=")); |
390 | 1 | } |
391 | | |
392 | | #[test] |
393 | 1 | fn test_compile_greater_ops() { |
394 | 1 | let result = compile("x > y && x >= z").unwrap(); |
395 | 1 | assert!(result.contains(">")); |
396 | 1 | assert!(result.contains(">=")); |
397 | 1 | } |
398 | | |
399 | | #[test] |
400 | 1 | fn test_compile_or_op() { |
401 | 1 | let result = compile("x || y").unwrap(); |
402 | 1 | assert!(result.contains("||")); |
403 | 1 | } |
404 | | |
405 | | #[test] |
406 | 1 | fn test_compile_complex_expression() { |
407 | 1 | let result = compile("(x + y) * (z - w) / 2").unwrap(); |
408 | 1 | assert!(result.contains("+")); |
409 | 1 | assert!(result.contains("-")); |
410 | 1 | assert!(result.contains("*")); |
411 | 1 | assert!(result.contains("/")); |
412 | 1 | } |
413 | | |
414 | | #[test] |
415 | 1 | fn test_compile_errors() { |
416 | 1 | assert!(compile("").is_err()); |
417 | 1 | assert!(compile(" ").is_err()); |
418 | 1 | assert!(compile("let x =").is_err()); |
419 | 1 | assert!(compile("if").is_err()); |
420 | 1 | assert!(compile("match").is_err()); |
421 | 1 | } |
422 | | |
423 | | #[test] |
424 | 1 | fn test_is_valid_syntax_valid_cases() { |
425 | 1 | assert!(is_valid_syntax("42")); |
426 | 1 | assert!(is_valid_syntax("3.14")); |
427 | 1 | assert!(is_valid_syntax("true")); |
428 | 1 | assert!(is_valid_syntax("false")); |
429 | 1 | assert!(is_valid_syntax("\"hello\"")); |
430 | 1 | assert!(is_valid_syntax("x + y")); |
431 | 1 | assert!(is_valid_syntax("[1, 2, 3]")); |
432 | 1 | assert!(is_valid_syntax("if true { 1 } else { 2 }")); |
433 | 1 | } |
434 | | |
435 | | #[test] |
436 | 1 | fn test_is_valid_syntax_invalid_cases() { |
437 | 1 | assert!(!is_valid_syntax("")); |
438 | 1 | assert!(!is_valid_syntax(" ")); |
439 | 1 | assert!(!is_valid_syntax("let x =")); |
440 | 1 | assert!(!is_valid_syntax("if { }")); |
441 | 1 | assert!(!is_valid_syntax("[1, 2,")); |
442 | 1 | assert!(!is_valid_syntax("match")); |
443 | 1 | assert!(!is_valid_syntax("struct")); |
444 | 1 | } |
445 | | |
446 | | #[test] |
447 | 1 | fn test_get_parse_error_with_errors() { |
448 | 1 | let error = get_parse_error("fun ("); |
449 | 1 | assert!(error.is_some()); |
450 | | // Error message format may vary, just check that we got an error |
451 | 1 | assert!(!error.unwrap().is_empty()); |
452 | 1 | } |
453 | | |
454 | | #[test] |
455 | 1 | fn test_get_parse_error_without_errors() { |
456 | 1 | let error = get_parse_error("42"); |
457 | 1 | assert!(error.is_none()); |
458 | 1 | } |
459 | | |
460 | | #[test] |
461 | 1 | fn test_get_parse_error_detailed() { |
462 | 1 | let error = get_parse_error("if"); |
463 | 1 | assert!(error.is_some()); |
464 | | |
465 | 1 | let error = get_parse_error("match"); |
466 | 1 | assert!(error.is_some()); |
467 | | |
468 | 1 | let error = get_parse_error("[1, 2,"); |
469 | 1 | assert!(error.is_some()); |
470 | 1 | } |
471 | | |
472 | | #[test] |
473 | 1 | fn test_compile_generic_function() { |
474 | 1 | let result = compile("fun id<T>(x: T) -> T { x }").unwrap(); |
475 | 1 | assert!(result.contains("fn")); |
476 | 1 | assert!(result.contains("id")); |
477 | 1 | } |
478 | | |
479 | | #[test] |
480 | 1 | fn test_compile_generic_struct() { |
481 | 1 | let result = compile("struct Box<T> { value: T }").unwrap(); |
482 | 1 | assert!(result.contains("struct")); |
483 | 1 | assert!(result.contains("Box")); |
484 | 1 | } |
485 | | |
486 | | #[test] |
487 | 1 | fn test_compile_multiple_statements() { |
488 | 1 | let result = compile("let x = 1 in let y = 2 in x + y").unwrap(); |
489 | 1 | assert!(result.contains("let")); |
490 | 1 | } |
491 | | |
492 | | #[test] |
493 | 1 | fn test_compile_pattern_matching() { |
494 | 1 | let result = compile("match x { 0 => \"zero\", _ => \"other\" }").unwrap(); |
495 | 1 | assert!(result.contains("match")); |
496 | 1 | } |
497 | | |
498 | | #[test] |
499 | 1 | fn test_compile_struct_literal() { |
500 | 1 | let result = compile("Point { x: 10, y: 20 }").unwrap(); |
501 | 1 | assert!(result.contains("Point")); |
502 | 1 | } |
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 | 1 | fn test_compile_await_expression() { |
513 | 1 | let result = compile("async_func().await").unwrap(); |
514 | 1 | assert!(result.contains("await")); |
515 | 1 | } |
516 | | |
517 | | #[test] |
518 | 1 | fn test_compile_import() { |
519 | 1 | let result = compile("import std.collections.HashMap").unwrap(); |
520 | 1 | assert!(result.contains("use")); |
521 | 1 | } |
522 | | |
523 | | #[test] |
524 | 1 | fn test_compile_while_loop() { |
525 | 1 | let result = compile("while x < 10 { x + 1 }").unwrap(); |
526 | 1 | assert!(result.contains("while")); |
527 | 1 | } |
528 | | |
529 | | #[test] |
530 | 1 | fn test_compile_range() { |
531 | 1 | let result = compile("1..10").unwrap(); |
532 | 1 | assert!(result.contains("..")); |
533 | 1 | } |
534 | | |
535 | | #[test] |
536 | 1 | fn test_compile_pipeline() { |
537 | 1 | let result = compile("data |> filter |> map").unwrap(); |
538 | 1 | assert!(result.contains("(")); |
539 | 1 | } |
540 | | |
541 | | #[test] |
542 | 1 | fn test_compile_send_operation() { |
543 | 1 | let result = compile("myactor <- message").unwrap(); |
544 | 1 | assert!(result.contains(". send (")); // Formatted with spaces |
545 | 1 | assert!(result.contains(". await")); // Formatted with spaces |
546 | 1 | } |
547 | | |
548 | | #[test] |
549 | 1 | fn test_compile_ask_operation() { |
550 | 1 | let result = compile("myactor <? request").unwrap(); |
551 | 1 | assert!(result.contains(". ask (")); // Formatted with spaces |
552 | 1 | assert!(result.contains(". await")); // Formatted with spaces |
553 | 1 | } |
554 | | |
555 | | #[test] |
556 | 1 | fn test_compile_list_comprehension() { |
557 | 1 | let result = compile("[x * 2 for x in range(10)]").unwrap(); |
558 | 1 | assert!(result.contains("map")); |
559 | 1 | } |
560 | | |
561 | | #[test] |
562 | 1 | fn test_compile_actor() { |
563 | 1 | let result = compile( |
564 | 1 | r" |
565 | 1 | actor Counter { |
566 | 1 | count: i32, |
567 | 1 | |
568 | 1 | receive { |
569 | 1 | Inc => 1, |
570 | 1 | Get => 0 |
571 | 1 | } |
572 | 1 | } |
573 | 1 | ", |
574 | | ) |
575 | 1 | .unwrap(); |
576 | 1 | assert!(result.contains("struct Counter")); |
577 | 1 | assert!(result.contains("enum CounterMessage")); |
578 | 1 | } |
579 | | |
580 | | // ===== COMPREHENSIVE COVERAGE TESTS ===== |
581 | | |
582 | | #[test] |
583 | 1 | fn test_type_conversions() { |
584 | | // String conversions |
585 | 1 | assert!(compile("str(42)").is_ok()); |
586 | 1 | assert!(compile("str(3.14)").is_ok()); |
587 | 1 | assert!(compile("str(true)").is_ok()); |
588 | | |
589 | | // Integer conversions |
590 | 1 | assert!(compile("int(\"42\")").is_ok()); |
591 | 1 | assert!(compile("int(3.14)").is_ok()); |
592 | 1 | assert!(compile("int(true)").is_ok()); |
593 | | |
594 | | // Float conversions |
595 | 1 | assert!(compile("float(\"3.14\")").is_ok()); |
596 | 1 | assert!(compile("float(42)").is_ok()); |
597 | | |
598 | | // Bool conversions |
599 | 1 | assert!(compile("bool(0)").is_ok()); |
600 | 1 | assert!(compile("bool(\"\")").is_ok()); |
601 | 1 | assert!(compile("bool([])").is_ok()); |
602 | | |
603 | | // Collection conversions |
604 | 1 | assert!(compile("list(\"hello\")").is_ok()); |
605 | 1 | assert!(compile("set([1,2,3])").is_ok()); |
606 | 1 | assert!(compile("dict([(\"a\",1)])").is_ok()); |
607 | 1 | } |
608 | | |
609 | | #[test] |
610 | 1 | fn test_method_calls() { |
611 | | // String methods |
612 | 1 | assert!(compile("\"hello\".upper()").is_ok()); |
613 | 1 | assert!(compile("\"HELLO\".lower()").is_ok()); |
614 | 1 | assert!(compile("\" hello \".strip()").is_ok()); |
615 | 1 | assert!(compile("\"hello\".len()").is_ok()); |
616 | 1 | assert!(compile("\"hello\".split(\" \")").is_ok()); |
617 | | |
618 | | // List methods |
619 | 1 | assert!(compile("[1,2,3].len()").is_ok()); |
620 | 1 | assert!(compile("[1,2,3].append(4)").is_ok()); |
621 | 1 | assert!(compile("[1,2,3].pop()").is_ok()); |
622 | 1 | assert!(compile("[1,2,3].reverse()").is_ok()); |
623 | 1 | assert!(compile("[1,2,3].sort()").is_ok()); |
624 | | |
625 | | // Dict methods |
626 | 1 | assert!(compile("{\"a\":1}.get(\"a\")").is_ok()); |
627 | 1 | assert!(compile("{\"a\":1}.keys()").is_ok()); |
628 | 1 | assert!(compile("{\"a\":1}.values()").is_ok()); |
629 | 1 | assert!(compile("{\"a\":1}.items()").is_ok()); |
630 | | |
631 | | // Iterator methods |
632 | 1 | assert!(compile("[1,2,3].map(|x| x*2)").is_ok()); |
633 | 1 | assert!(compile("[1,2,3].filter(|x| x>1)").is_ok()); |
634 | 1 | assert!(compile("[1,2,3].reduce(|a,b| a+b)").is_ok()); |
635 | 1 | } |
636 | | |
637 | | #[test] |
638 | | #[ignore = "Patterns not fully implemented"] |
639 | 0 | fn test_patterns() { |
640 | | // Literal patterns |
641 | 0 | assert!(compile("match x { 0 => \"zero\", _ => \"other\" }").is_ok()); |
642 | 0 | assert!(compile("match x { true => \"yes\", false => \"no\" }").is_ok()); |
643 | | |
644 | | // Tuple patterns |
645 | 0 | assert!(compile("match p { (0, 0) => \"origin\", _ => \"other\" }").is_ok()); |
646 | 0 | assert!(compile("match p { (x, y) => x + y }").is_ok()); |
647 | | |
648 | | // List patterns |
649 | 0 | assert!(compile("match lst { [] => \"empty\", _ => \"has items\" }").is_ok()); |
650 | 0 | assert!(compile("match lst { [x] => x, _ => 0 }").is_ok()); |
651 | 0 | assert!(compile("match lst { [head, ...tail] => head, _ => 0 }").is_ok()); |
652 | | |
653 | | // Struct patterns |
654 | 0 | assert!(compile("match p { Point { x, y } => x + y }").is_ok()); |
655 | | |
656 | | // Enum patterns |
657 | 0 | assert!(compile("match opt { Some(x) => x, None => 0 }").is_ok()); |
658 | 0 | assert!(compile("match res { Ok(v) => v, Err(e) => panic(e) }").is_ok()); |
659 | | |
660 | | // Guard patterns |
661 | 0 | assert!(compile("match x { n if n > 0 => \"positive\", _ => \"other\" }").is_ok()); |
662 | | |
663 | | // Or patterns |
664 | 0 | assert!(compile("match x { 0 | 1 => \"binary\", _ => \"other\" }").is_ok()); |
665 | 0 | } |
666 | | |
667 | | #[test] |
668 | | #[ignore = "Not all operators implemented yet"] |
669 | 0 | fn test_all_operators() { |
670 | | // Arithmetic |
671 | 0 | assert!(compile("x + y").is_ok()); |
672 | 0 | assert!(compile("x - y").is_ok()); |
673 | 0 | assert!(compile("x * y").is_ok()); |
674 | 0 | assert!(compile("x / y").is_ok()); |
675 | 0 | assert!(compile("x % y").is_ok()); |
676 | 0 | assert!(compile("x ** y").is_ok()); |
677 | | |
678 | | // Comparison |
679 | 0 | assert!(compile("x == y").is_ok()); |
680 | 0 | assert!(compile("x != y").is_ok()); |
681 | 0 | assert!(compile("x < y").is_ok()); |
682 | 0 | assert!(compile("x > y").is_ok()); |
683 | 0 | assert!(compile("x <= y").is_ok()); |
684 | 0 | assert!(compile("x >= y").is_ok()); |
685 | | |
686 | | // Logical |
687 | 0 | assert!(compile("x && y").is_ok()); |
688 | 0 | assert!(compile("x || y").is_ok()); |
689 | 0 | assert!(compile("!x").is_ok()); |
690 | | |
691 | | // Bitwise |
692 | 0 | assert!(compile("x & y").is_ok()); |
693 | 0 | assert!(compile("x | y").is_ok()); |
694 | 0 | assert!(compile("x ^ y").is_ok()); |
695 | 0 | assert!(compile("~x").is_ok()); |
696 | 0 | assert!(compile("x << y").is_ok()); |
697 | 0 | assert!(compile("x >> y").is_ok()); |
698 | | |
699 | | // Assignment |
700 | 0 | assert!(compile("x = 5").is_ok()); |
701 | 0 | assert!(compile("x += 5").is_ok()); |
702 | 0 | assert!(compile("x -= 5").is_ok()); |
703 | 0 | assert!(compile("x *= 5").is_ok()); |
704 | 0 | assert!(compile("x /= 5").is_ok()); |
705 | | |
706 | | // Special |
707 | 0 | assert!(compile("x ?? y").is_ok()); |
708 | 0 | assert!(compile("x?.y").is_ok()); |
709 | 0 | } |
710 | | |
711 | | #[test] |
712 | | #[ignore = "Control flow not fully implemented"] |
713 | 0 | fn test_control_flow() { |
714 | | // If statements |
715 | 0 | assert!(compile("if x { 1 }").is_ok()); |
716 | 0 | assert!(compile("if x { 1 } else { 2 }").is_ok()); |
717 | 0 | assert!(compile("if x { 1 } else if y { 2 } else { 3 }").is_ok()); |
718 | | |
719 | | // Loops |
720 | 0 | assert!(compile("while x { y }").is_ok()); |
721 | 0 | assert!(compile("loop { break }").is_ok()); |
722 | 0 | assert!(compile("for i in 0..10 { }").is_ok()); |
723 | 0 | assert!(compile("for i in items { }").is_ok()); |
724 | | |
725 | | // Break/continue |
726 | 0 | assert!(compile("while true { break }").is_ok()); |
727 | 0 | assert!(compile("for i in 0..10 { continue }").is_ok()); |
728 | 0 | } |
729 | | |
730 | | #[test] |
731 | | #[ignore = "Data structures not fully implemented"] |
732 | 0 | fn test_data_structures() { |
733 | | // Lists |
734 | 0 | assert!(compile("[]").is_ok()); |
735 | 0 | assert!(compile("[1, 2, 3]").is_ok()); |
736 | 0 | assert!(compile("[[1, 2], [3, 4]]").is_ok()); |
737 | | |
738 | | // Dicts |
739 | 0 | assert!(compile("{}").is_ok()); |
740 | 0 | assert!(compile("{\"a\": 1}").is_ok()); |
741 | 0 | assert!(compile("{\"a\": 1, \"b\": 2}").is_ok()); |
742 | | |
743 | | // Sets |
744 | 0 | assert!(compile("{1}").is_ok()); |
745 | 0 | assert!(compile("{1, 2, 3}").is_ok()); |
746 | | |
747 | | // Tuples |
748 | 0 | assert!(compile("()").is_ok()); |
749 | 0 | assert!(compile("(1,)").is_ok()); |
750 | 0 | assert!(compile("(1, 2, 3)").is_ok()); |
751 | 0 | } |
752 | | |
753 | | #[test] |
754 | | #[ignore = "Functions not fully implemented"] |
755 | 0 | fn test_functions_lambdas() { |
756 | | // Functions |
757 | 0 | assert!(compile("fn f() { }").is_ok()); |
758 | 0 | assert!(compile("fn f(x) { x }").is_ok()); |
759 | 0 | assert!(compile("fn f(x, y) { x + y }").is_ok()); |
760 | 0 | assert!(compile("fn f(x: int) -> int { x }").is_ok()); |
761 | | |
762 | | // Lambdas |
763 | 0 | assert!(compile("|x| x").is_ok()); |
764 | 0 | assert!(compile("|x, y| x + y").is_ok()); |
765 | 0 | assert!(compile("|| 42").is_ok()); |
766 | | |
767 | | // Async |
768 | 0 | assert!(compile("async fn f() { await g() }").is_ok()); |
769 | 0 | assert!(compile("await fetch(url)").is_ok()); |
770 | 0 | } |
771 | | |
772 | | #[test] |
773 | 1 | fn test_string_interpolation() { |
774 | 1 | assert!(compile("f\"Hello {name}\"").is_ok()); |
775 | 1 | assert!(compile("f\"x = {x}, y = {y}\"").is_ok()); |
776 | 1 | assert!(compile("f\"Result: {calculate()}\"").is_ok()); |
777 | 1 | } |
778 | | |
779 | | #[test] |
780 | | #[ignore = "Comprehensions not fully implemented"] |
781 | 0 | fn test_comprehensions() { |
782 | 0 | assert!(compile("[x * 2 for x in 0..10]").is_ok()); |
783 | 0 | assert!(compile("[x for x in items if x > 0]").is_ok()); |
784 | 0 | assert!(compile("{x: x*x for x in 0..5}").is_ok()); |
785 | 0 | assert!(compile("{x for x in items if unique(x)}").is_ok()); |
786 | 0 | } |
787 | | |
788 | | #[test] |
789 | | #[ignore = "Destructuring not fully implemented"] |
790 | 0 | fn test_destructuring() { |
791 | 0 | assert!(compile("let [a, b, c] = [1, 2, 3]").is_ok()); |
792 | 0 | assert!(compile("let {x, y} = point").is_ok()); |
793 | 0 | assert!(compile("let [head, ...tail] = list").is_ok()); |
794 | 0 | assert!(compile("let (a, b) = (1, 2)").is_ok()); |
795 | 0 | } |
796 | | |
797 | | #[test] |
798 | | #[ignore = "Error handling not fully implemented"] |
799 | 0 | fn test_error_handling() { |
800 | 0 | assert!(compile("try { risky() } catch e { handle(e) }").is_ok()); |
801 | 0 | assert!(compile("result?").is_ok()); |
802 | 0 | assert!(compile("result.unwrap()").is_ok()); |
803 | 0 | assert!(compile("result.expect(\"failed\")").is_ok()); |
804 | 0 | assert!(compile("result.unwrap_or(default)").is_ok()); |
805 | 0 | } |
806 | | |
807 | | #[test] |
808 | | #[ignore = "Classes/structs not fully implemented"] |
809 | 0 | fn test_classes_structs() { |
810 | 0 | assert!(compile("struct Point { x: int, y: int }").is_ok()); |
811 | 0 | assert!(compile("class Calculator { fn add(x, y) { x + y } }").is_ok()); |
812 | 0 | assert!(compile("enum Option { Some(value), None }").is_ok()); |
813 | 0 | } |
814 | | |
815 | | #[test] |
816 | | #[ignore = "Imports not fully implemented"] |
817 | 0 | fn test_imports() { |
818 | 0 | assert!(compile("import std").is_ok()); |
819 | 0 | assert!(compile("from std import println").is_ok()); |
820 | 0 | assert!(compile("import { readFile, writeFile } from fs").is_ok()); |
821 | 0 | assert!(compile("export fn helper()").is_ok()); |
822 | 0 | } |
823 | | |
824 | | #[test] |
825 | 1 | fn test_decorators() { |
826 | 1 | assert!(compile("@memoize\nfn expensive(n) { }").is_ok()); |
827 | 1 | assert!(compile("@derive(Debug, Clone)\nstruct Data { }").is_ok()); |
828 | 1 | } |
829 | | |
830 | | #[test] |
831 | 1 | fn test_generics() { |
832 | 1 | assert!(compile("fn identity<T>(x: T) -> T { x }").is_ok()); |
833 | 1 | assert!(compile("struct Pair<T, U> { first: T, second: U }").is_ok()); |
834 | 1 | assert!(compile("enum Result<T, E> { Ok(T), Err(E) }").is_ok()); |
835 | 1 | } |
836 | | |
837 | | #[test] |
838 | 1 | fn test_edge_cases() { |
839 | | // Empty input - parser expects at least one expression |
840 | 1 | assert!(!is_valid_syntax("")); |
841 | 1 | assert!(!is_valid_syntax(" ")); |
842 | 1 | assert!(!is_valid_syntax("\n\n")); |
843 | | |
844 | | // Deeply nested |
845 | 1 | assert!(compile("((((((((((1))))))))))").is_ok()); |
846 | 1 | assert!(compile("[[[[[[1]]]]]]").is_ok()); |
847 | | |
848 | | // Unicode |
849 | 1 | assert!(compile("\"Hello δΈη\"").is_ok()); |
850 | 1 | assert!(compile("\"Emoji π\"").is_ok()); |
851 | 1 | } |
852 | | |
853 | | #[test] |
854 | 1 | fn test_complex_programs() { |
855 | 1 | let factorial = r#" |
856 | 1 | fn factorial(n) { |
857 | 1 | if n <= 1 { 1 } else { n * factorial(n-1) } |
858 | 1 | } |
859 | 1 | "#; |
860 | 1 | assert!(compile(factorial).is_ok()); |
861 | | |
862 | 1 | let fibonacci = r#" |
863 | 1 | fn fibonacci(n) { |
864 | 1 | match n { |
865 | 1 | 0 => 0, |
866 | 1 | 1 => 1, |
867 | 1 | _ => fibonacci(n-1) + fibonacci(n-2) |
868 | 1 | } |
869 | 1 | } |
870 | 1 | "#; |
871 | 1 | assert!(compile(fibonacci).is_ok()); |
872 | | |
873 | 1 | let quicksort = r#" |
874 | 1 | fn quicksort(arr) { |
875 | 1 | if arr.len() <= 1 { |
876 | 1 | arr |
877 | 1 | } else { |
878 | 1 | let pivot = arr[0] |
879 | 1 | let less = [x for x in arr[1:] if x < pivot] |
880 | 1 | let greater = [x for x in arr[1:] if x >= pivot] |
881 | 1 | quicksort(less) + [pivot] + quicksort(greater) |
882 | 1 | } |
883 | 1 | } |
884 | 1 | "#; |
885 | 1 | assert!(compile(quicksort).is_ok()); |
886 | 1 | } |
887 | | } |