1use std::collections::{HashMap, HashSet};
74use std::fs;
75use std::io::Write;
76use std::path::Path;
77use std::process::Command;
78
79const CRATES_DATA_PATH: &str = "crates/logicaffeine_data";
81const CRATES_SYSTEM_PATH: &str = "crates/logicaffeine_system";
82
83use std::fmt::Write as FmtWrite;
84
85use crate::analysis::{DiscoveryPass, EscapeChecker, OwnershipChecker, PolicyRegistry};
86use crate::arena::Arena;
87use crate::arena_ctx::AstContext;
88use crate::ast::{Expr, MatchArm, Stmt, TypeExpr};
89use crate::ast::stmt::{BinaryOpKind, ClosureBody, Literal, Pattern, ReadSource, SelectBranch, StringPart};
90use crate::codegen::{codegen_program, generate_c_header, generate_python_bindings, generate_typescript_bindings};
91use crate::diagnostic::{parse_rustc_json, translate_diagnostics, LogosError};
92use crate::drs::WorldState;
93use crate::error::ParseError;
94use crate::intern::Interner;
95use crate::lexer::Lexer;
96use crate::parser::Parser;
97use crate::sourcemap::SourceMap;
98
99#[derive(Debug, Clone)]
101pub struct CrateDependency {
102 pub name: String,
103 pub version: String,
104 pub features: Vec<String>,
105}
106
107#[derive(Debug)]
109pub struct CompileOutput {
110 pub rust_code: String,
111 pub dependencies: Vec<CrateDependency>,
112 pub c_header: Option<String>,
114 pub python_bindings: Option<String>,
116 pub typescript_types: Option<String>,
118 pub typescript_bindings: Option<String>,
120}
121
122pub fn interpret_program(source: &str) -> Result<String, ParseError> {
140 let result = crate::ui_bridge::interpret_for_ui_sync(source);
141 if let Some(err) = result.error {
142 Err(ParseError {
143 kind: crate::error::ParseErrorKind::Custom(err),
144 span: crate::token::Span::default(),
145 })
146 } else {
147 Ok(result.lines.join("\n"))
148 }
149}
150
151pub fn compile_to_rust(source: &str) -> Result<String, ParseError> {
184 compile_program_full(source).map(|o| o.rust_code)
185}
186
187pub fn compile_to_c(source: &str) -> Result<String, ParseError> {
192 let mut interner = Interner::new();
193 let mut lexer = Lexer::new(source, &mut interner);
194 let tokens = lexer.tokenize();
195
196 let (type_registry, _policy_registry) = {
197 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
198 let result = discovery.run_full();
199 (result.types, result.policies)
200 };
201 let codegen_registry = type_registry.clone();
202
203 let mut world_state = WorldState::new();
204 let expr_arena = Arena::new();
205 let term_arena = Arena::new();
206 let np_arena = Arena::new();
207 let sym_arena = Arena::new();
208 let role_arena = Arena::new();
209 let pp_arena = Arena::new();
210 let stmt_arena: Arena<Stmt> = Arena::new();
211 let imperative_expr_arena: Arena<Expr> = Arena::new();
212 let type_expr_arena: Arena<TypeExpr> = Arena::new();
213
214 let ast_ctx = AstContext::with_types(
215 &expr_arena, &term_arena, &np_arena, &sym_arena,
216 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
217 &type_expr_arena,
218 );
219
220 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
221 let stmts = parser.parse_program()?;
222 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
223
224 Ok(crate::codegen_c::codegen_program_c(&stmts, &codegen_registry, &interner))
225}
226
227pub fn compile_program_full(source: &str) -> Result<CompileOutput, ParseError> {
232 let mut interner = Interner::new();
233 let mut lexer = Lexer::new(source, &mut interner);
234 let tokens = lexer.tokenize();
235
236 let (type_registry, policy_registry) = {
238 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
239 let result = discovery.run_full();
240 (result.types, result.policies)
241 };
242 let codegen_registry = type_registry.clone();
244 let codegen_policies = policy_registry.clone();
245
246 let mut world_state = WorldState::new();
247 let expr_arena = Arena::new();
248 let term_arena = Arena::new();
249 let np_arena = Arena::new();
250 let sym_arena = Arena::new();
251 let role_arena = Arena::new();
252 let pp_arena = Arena::new();
253 let stmt_arena: Arena<Stmt> = Arena::new();
254 let imperative_expr_arena: Arena<Expr> = Arena::new();
255 let type_expr_arena: Arena<TypeExpr> = Arena::new();
256
257 let ast_ctx = AstContext::with_types(
258 &expr_arena,
259 &term_arena,
260 &np_arena,
261 &sym_arena,
262 &role_arena,
263 &pp_arena,
264 &stmt_arena,
265 &imperative_expr_arena,
266 &type_expr_arena,
267 );
268
269 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
271 let stmts = parser.parse_program()?;
274
275 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
277
278 let mut dependencies = extract_dependencies(&stmts, &interner)?;
280
281 let needs_wasm_bindgen = stmts.iter().any(|stmt| {
283 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
284 interner.resolve(*target).eq_ignore_ascii_case("wasm")
285 } else {
286 false
287 }
288 });
289 if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
290 dependencies.push(CrateDependency {
291 name: "wasm-bindgen".to_string(),
292 version: "0.2".to_string(),
293 features: vec![],
294 });
295 }
296
297 let mut escape_checker = EscapeChecker::new(&interner);
300 escape_checker.check_program(&stmts).map_err(|e| {
301 ParseError {
304 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
305 span: e.span,
306 }
307 })?;
308
309 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
313 .map_err(|e| ParseError {
314 kind: e.to_parse_error_kind(&interner),
315 span: crate::token::Span::default(),
316 })?;
317 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
318
319 let has_c = stmts.iter().any(|stmt| {
321 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
322 match export_target {
323 None => true,
324 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
325 }
326 } else {
327 false
328 }
329 });
330
331 let c_header = if has_c {
332 Some(generate_c_header(&stmts, "module", &interner, &codegen_registry))
333 } else {
334 None
335 };
336
337 if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
339 dependencies.push(CrateDependency {
340 name: "serde_json".to_string(),
341 version: "1".to_string(),
342 features: vec![],
343 });
344 }
345
346 let python_bindings = if has_c {
347 Some(generate_python_bindings(&stmts, "module", &interner, &codegen_registry))
348 } else {
349 None
350 };
351
352 let (typescript_bindings, typescript_types) = if has_c {
353 let (js, dts) = generate_typescript_bindings(&stmts, "module", &interner, &codegen_registry);
354 (Some(js), Some(dts))
355 } else {
356 (None, None)
357 };
358
359 Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
360}
361
362fn extract_dependencies(stmts: &[Stmt], interner: &Interner) -> Result<Vec<CrateDependency>, ParseError> {
368 use std::collections::HashMap;
369
370 let mut seen: HashMap<String, String> = HashMap::new(); let mut deps: Vec<CrateDependency> = Vec::new();
372
373 for stmt in stmts {
374 if let Stmt::Require { crate_name, version, features, span } = stmt {
375 let name = interner.resolve(*crate_name).to_string();
376 let ver = interner.resolve(*version).to_string();
377
378 if let Some(existing_ver) = seen.get(&name) {
379 if *existing_ver != ver {
380 return Err(ParseError {
381 kind: crate::error::ParseErrorKind::Custom(format!(
382 "Conflicting versions for crate \"{}\": \"{}\" and \"{}\".",
383 name, existing_ver, ver
384 )),
385 span: *span,
386 });
387 }
388 } else {
390 seen.insert(name.clone(), ver.clone());
391 deps.push(CrateDependency {
392 name,
393 version: ver,
394 features: features.iter().map(|f| interner.resolve(*f).to_string()).collect(),
395 });
396 }
397 }
398 }
399
400 Ok(deps)
401}
402
403pub fn compile_to_rust_checked(source: &str) -> Result<String, ParseError> {
439 let mut interner = Interner::new();
440 let mut lexer = Lexer::new(source, &mut interner);
441 let tokens = lexer.tokenize();
442
443 let (type_registry, policy_registry) = {
445 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
446 let result = discovery.run_full();
447 (result.types, result.policies)
448 };
449 let codegen_registry = type_registry.clone();
451 let codegen_policies = policy_registry.clone();
452
453 let mut world_state = WorldState::new();
454 let expr_arena = Arena::new();
455 let term_arena = Arena::new();
456 let np_arena = Arena::new();
457 let sym_arena = Arena::new();
458 let role_arena = Arena::new();
459 let pp_arena = Arena::new();
460 let stmt_arena: Arena<Stmt> = Arena::new();
461 let imperative_expr_arena: Arena<Expr> = Arena::new();
462 let type_expr_arena: Arena<TypeExpr> = Arena::new();
463
464 let ast_ctx = AstContext::with_types(
465 &expr_arena,
466 &term_arena,
467 &np_arena,
468 &sym_arena,
469 &role_arena,
470 &pp_arena,
471 &stmt_arena,
472 &imperative_expr_arena,
473 &type_expr_arena,
474 );
475
476 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
478 let stmts = parser.parse_program()?;
479
480 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
482
483 let mut escape_checker = EscapeChecker::new(&interner);
485 escape_checker.check_program(&stmts).map_err(|e| {
486 ParseError {
487 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
488 span: e.span,
489 }
490 })?;
491
492 let mut ownership_checker = OwnershipChecker::new(&interner);
495 ownership_checker.check_program(&stmts).map_err(|e| {
496 ParseError {
497 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
498 span: e.span,
499 }
500 })?;
501
502 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
503 .map_err(|e| ParseError {
504 kind: e.to_parse_error_kind(&interner),
505 span: crate::token::Span::default(),
506 })?;
507 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
508
509 Ok(rust_code)
510}
511
512#[cfg(feature = "verification")]
559pub fn compile_to_rust_verified(source: &str) -> Result<String, ParseError> {
560 use crate::verification::VerificationPass;
561
562 let mut interner = Interner::new();
563 let mut lexer = Lexer::new(source, &mut interner);
564 let tokens = lexer.tokenize();
565
566 let (type_registry, policy_registry) = {
568 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
569 let result = discovery.run_full();
570 (result.types, result.policies)
571 };
572 let codegen_registry = type_registry.clone();
574 let codegen_policies = policy_registry.clone();
575
576 let mut world_state = WorldState::new();
577 let expr_arena = Arena::new();
578 let term_arena = Arena::new();
579 let np_arena = Arena::new();
580 let sym_arena = Arena::new();
581 let role_arena = Arena::new();
582 let pp_arena = Arena::new();
583 let stmt_arena: Arena<Stmt> = Arena::new();
584 let imperative_expr_arena: Arena<Expr> = Arena::new();
585 let type_expr_arena: Arena<TypeExpr> = Arena::new();
586
587 let ast_ctx = AstContext::with_types(
588 &expr_arena,
589 &term_arena,
590 &np_arena,
591 &sym_arena,
592 &role_arena,
593 &pp_arena,
594 &stmt_arena,
595 &imperative_expr_arena,
596 &type_expr_arena,
597 );
598
599 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
601 let stmts = parser.parse_program()?;
602
603 let mut escape_checker = EscapeChecker::new(&interner);
605 escape_checker.check_program(&stmts).map_err(|e| {
606 ParseError {
607 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
608 span: e.span,
609 }
610 })?;
611
612 let mut verifier = VerificationPass::new(&interner);
614 verifier.verify_program(&stmts).map_err(|e| {
615 ParseError {
616 kind: crate::error::ParseErrorKind::Custom(format!(
617 "Verification Failed:\n\n{}",
618 e
619 )),
620 span: crate::token::Span::default(),
621 }
622 })?;
623
624 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
625 .map_err(|e| ParseError {
626 kind: e.to_parse_error_kind(&interner),
627 span: crate::token::Span::default(),
628 })?;
629 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
630
631 Ok(rust_code)
632}
633
634pub fn compile_to_dir(source: &str, output_dir: &Path) -> Result<(), CompileError> {
665 let output = compile_program_full(source).map_err(CompileError::Parse)?;
666
667 let src_dir = output_dir.join("src");
669 fs::create_dir_all(&src_dir).map_err(|e| CompileError::Io(e.to_string()))?;
670
671 let main_path = src_dir.join("main.rs");
673 let mut file = fs::File::create(&main_path).map_err(|e| CompileError::Io(e.to_string()))?;
674 file.write_all(output.rust_code.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
675
676 let mut cargo_toml = String::from(r#"[package]
678name = "logos_output"
679version = "0.1.0"
680edition = "2021"
681
682[dependencies]
683logicaffeine-data = { path = "./crates/logicaffeine_data" }
684logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full"] }
685tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
686
687[target.'cfg(target_os = "linux")'.dependencies]
688logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full", "io-uring"] }
689"#);
690
691 for dep in &output.dependencies {
693 if dep.features.is_empty() {
694 let _ = writeln!(cargo_toml, "{} = \"{}\"", dep.name, dep.version);
695 } else {
696 let feats = dep.features.iter()
697 .map(|f| format!("\"{}\"", f))
698 .collect::<Vec<_>>()
699 .join(", ");
700 let _ = writeln!(
701 cargo_toml,
702 "{} = {{ version = \"{}\", features = [{}] }}",
703 dep.name, dep.version, feats
704 );
705 }
706 }
707
708 cargo_toml.push_str("\n[profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\npanic = \"abort\"\nstrip = true\n");
709
710 let cargo_path = output_dir.join("Cargo.toml");
711 let mut file = fs::File::create(&cargo_path).map_err(|e| CompileError::Io(e.to_string()))?;
712 file.write_all(cargo_toml.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
713
714 let cargo_config_dir = output_dir.join(".cargo");
717 fs::create_dir_all(&cargo_config_dir).map_err(|e| CompileError::Io(e.to_string()))?;
718 let config_content = "[build]\nrustflags = [\"-C\", \"target-cpu=native\"]\n";
719 let config_path = cargo_config_dir.join("config.toml");
720 fs::write(&config_path, config_content).map_err(|e| CompileError::Io(e.to_string()))?;
721
722 copy_runtime_crates(output_dir)?;
724
725 Ok(())
726}
727
728pub fn copy_runtime_crates(output_dir: &Path) -> Result<(), CompileError> {
731 let crates_dir = output_dir.join("crates");
732 fs::create_dir_all(&crates_dir).map_err(|e| CompileError::Io(e.to_string()))?;
733
734 let workspace_root = find_workspace_root()?;
736
737 let data_src = workspace_root.join(CRATES_DATA_PATH);
739 let data_dest = crates_dir.join("logicaffeine_data");
740 copy_dir_recursive(&data_src, &data_dest)?;
741 deworkspace_cargo_toml(&data_dest.join("Cargo.toml"))?;
742
743 let system_src = workspace_root.join(CRATES_SYSTEM_PATH);
745 let system_dest = crates_dir.join("logicaffeine_system");
746 copy_dir_recursive(&system_src, &system_dest)?;
747 deworkspace_cargo_toml(&system_dest.join("Cargo.toml"))?;
748
749 let base_src = workspace_root.join("crates/logicaffeine_base");
751 let base_dest = crates_dir.join("logicaffeine_base");
752 copy_dir_recursive(&base_src, &base_dest)?;
753 deworkspace_cargo_toml(&base_dest.join("Cargo.toml"))?;
754
755 Ok(())
756}
757
758fn deworkspace_cargo_toml(cargo_toml_path: &Path) -> Result<(), CompileError> {
764 let content = fs::read_to_string(cargo_toml_path)
765 .map_err(|e| CompileError::Io(e.to_string()))?;
766
767 let mut result = String::with_capacity(content.len());
768 for line in content.lines() {
769 let trimmed = line.trim();
770 if trimmed == "edition.workspace = true" {
771 result.push_str("edition = \"2021\"");
772 } else if trimmed == "rust-version.workspace = true" {
773 result.push_str("rust-version = \"1.75\"");
774 } else if trimmed == "authors.workspace = true"
775 || trimmed == "repository.workspace = true"
776 || trimmed == "homepage.workspace = true"
777 || trimmed == "documentation.workspace = true"
778 || trimmed == "keywords.workspace = true"
779 || trimmed == "categories.workspace = true"
780 || trimmed == "license.workspace = true"
781 {
782 continue;
784 } else if trimmed.contains(".workspace = true") {
785 continue;
787 } else {
788 result.push_str(line);
789 }
790 result.push('\n');
791 }
792
793 fs::write(cargo_toml_path, result)
794 .map_err(|e| CompileError::Io(e.to_string()))?;
795
796 Ok(())
797}
798
799fn find_workspace_root() -> Result<std::path::PathBuf, CompileError> {
801 if let Ok(workspace) = std::env::var("LOGOS_WORKSPACE") {
803 let path = Path::new(&workspace);
804 if path.join("Cargo.toml").exists() && path.join("crates").exists() {
805 return Ok(path.to_path_buf());
806 }
807 }
808
809 if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
811 let path = Path::new(&manifest_dir);
812 if let Some(parent) = path.parent().and_then(|p| p.parent()) {
813 if parent.join("Cargo.toml").exists() {
814 return Ok(parent.to_path_buf());
815 }
816 }
817 }
818
819 if let Ok(exe) = std::env::current_exe() {
822 if let Some(dir) = exe.parent() {
823 let mut candidate = dir.to_path_buf();
825 for _ in 0..5 {
826 if candidate.join("Cargo.toml").exists() && candidate.join("crates").exists() {
827 return Ok(candidate);
828 }
829 if !candidate.pop() {
830 break;
831 }
832 }
833 }
834 }
835
836 let mut current = std::env::current_dir()
838 .map_err(|e| CompileError::Io(e.to_string()))?;
839
840 loop {
841 if current.join("Cargo.toml").exists() && current.join("crates").exists() {
842 return Ok(current);
843 }
844 if !current.pop() {
845 return Err(CompileError::Io(
846 "Could not find workspace root. Set LOGOS_WORKSPACE env var or run from within the workspace.".to_string()
847 ));
848 }
849 }
850}
851
852fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), CompileError> {
855 fs::create_dir_all(dst).map_err(|e| CompileError::Io(e.to_string()))?;
856
857 for entry in fs::read_dir(src).map_err(|e| CompileError::Io(e.to_string()))? {
858 let entry = entry.map_err(|e| CompileError::Io(e.to_string()))?;
859 let src_path = entry.path();
860 let file_name = entry.file_name();
861 let dst_path = dst.join(&file_name);
862
863 if file_name == "target"
865 || file_name == ".git"
866 || file_name == "Cargo.lock"
867 || file_name == ".DS_Store"
868 {
869 continue;
870 }
871
872 if file_name.to_string_lossy().starts_with('.') {
874 continue;
875 }
876
877 if !src_path.exists() {
879 continue;
880 }
881
882 if src_path.is_dir() {
883 copy_dir_recursive(&src_path, &dst_path)?;
884 } else if file_name == "Cargo.toml" {
885 match fs::read_to_string(&src_path) {
888 Ok(content) => {
889 let filtered: String = content
890 .lines()
891 .filter(|line| !line.trim().starts_with("[workspace]"))
892 .collect::<Vec<_>>()
893 .join("\n");
894 fs::write(&dst_path, filtered)
895 .map_err(|e| CompileError::Io(e.to_string()))?;
896 }
897 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
898 Err(e) => return Err(CompileError::Io(e.to_string())),
899 }
900 } else {
901 match fs::copy(&src_path, &dst_path) {
902 Ok(_) => {}
903 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
904 Err(e) => return Err(CompileError::Io(e.to_string())),
905 }
906 }
907 }
908
909 Ok(())
910}
911
912pub fn compile_and_run(source: &str, output_dir: &Path) -> Result<String, CompileError> {
955 compile_to_rust_checked(source).map_err(CompileError::Parse)?;
958
959 compile_to_dir(source, output_dir)?;
960
961 let build_output = Command::new("cargo")
963 .arg("build")
964 .arg("--message-format=json")
965 .current_dir(output_dir)
966 .output()
967 .map_err(|e| CompileError::Io(e.to_string()))?;
968
969 if !build_output.status.success() {
970 let stderr = String::from_utf8_lossy(&build_output.stderr);
971 let stdout = String::from_utf8_lossy(&build_output.stdout);
972
973 let diagnostics = parse_rustc_json(&stdout);
975
976 if !diagnostics.is_empty() {
977 let source_map = SourceMap::new(source.to_string());
979 let interner = Interner::new();
980
981 if let Some(logos_error) = translate_diagnostics(&diagnostics, &source_map, &interner) {
982 return Err(CompileError::Ownership(logos_error));
983 }
984 }
985
986 return Err(CompileError::Build(stderr.to_string()));
988 }
989
990 let run_output = Command::new("cargo")
992 .arg("run")
993 .arg("--quiet")
994 .current_dir(output_dir)
995 .output()
996 .map_err(|e| CompileError::Io(e.to_string()))?;
997
998 if !run_output.status.success() {
999 let stderr = String::from_utf8_lossy(&run_output.stderr);
1000 return Err(CompileError::Runtime(stderr.to_string()));
1001 }
1002
1003 let stdout = String::from_utf8_lossy(&run_output.stdout);
1004 Ok(stdout.to_string())
1005}
1006
1007pub fn compile_file(path: &Path) -> Result<String, CompileError> {
1010 let source = fs::read_to_string(path).map_err(|e| CompileError::Io(e.to_string()))?;
1011 compile_to_rust(&source).map_err(CompileError::Parse)
1012}
1013
1014pub fn compile_project(entry_file: &Path) -> Result<CompileOutput, CompileError> {
1032 use crate::loader::Loader;
1033 use crate::analysis::discover_with_imports;
1034
1035 let root_path = entry_file.parent().unwrap_or(Path::new(".")).to_path_buf();
1036 let mut loader = Loader::new(root_path);
1037 let mut interner = Interner::new();
1038
1039 let source = fs::read_to_string(entry_file)
1041 .map_err(|e| CompileError::Io(format!("Failed to read entry file: {}", e)))?;
1042
1043 let type_registry = discover_with_imports(entry_file, &source, &mut loader, &mut interner)
1045 .map_err(|e| CompileError::Io(e))?;
1046
1047 compile_to_rust_with_registry_full(&source, type_registry, &mut interner)
1049 .map_err(CompileError::Parse)
1050}
1051
1052fn compile_to_rust_with_registry_full(
1055 source: &str,
1056 type_registry: crate::analysis::TypeRegistry,
1057 interner: &mut Interner,
1058) -> Result<CompileOutput, ParseError> {
1059 let mut lexer = Lexer::new(source, interner);
1060 let tokens = lexer.tokenize();
1061
1062 let policy_registry = {
1064 let mut discovery = DiscoveryPass::new(&tokens, interner);
1065 discovery.run_full().policies
1066 };
1067
1068 let codegen_registry = type_registry.clone();
1069 let codegen_policies = policy_registry.clone();
1070
1071 let mut world_state = WorldState::new();
1072 let expr_arena = Arena::new();
1073 let term_arena = Arena::new();
1074 let np_arena = Arena::new();
1075 let sym_arena = Arena::new();
1076 let role_arena = Arena::new();
1077 let pp_arena = Arena::new();
1078 let stmt_arena: Arena<Stmt> = Arena::new();
1079 let imperative_expr_arena: Arena<Expr> = Arena::new();
1080 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1081
1082 let ast_ctx = AstContext::with_types(
1083 &expr_arena,
1084 &term_arena,
1085 &np_arena,
1086 &sym_arena,
1087 &role_arena,
1088 &pp_arena,
1089 &stmt_arena,
1090 &imperative_expr_arena,
1091 &type_expr_arena,
1092 );
1093
1094 let mut parser = Parser::new(tokens, &mut world_state, interner, ast_ctx, type_registry);
1095 let stmts = parser.parse_program()?;
1096
1097 let mut dependencies = extract_dependencies(&stmts, interner)?;
1099
1100 let needs_wasm_bindgen = stmts.iter().any(|stmt| {
1102 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
1103 interner.resolve(*target).eq_ignore_ascii_case("wasm")
1104 } else {
1105 false
1106 }
1107 });
1108 if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
1109 dependencies.push(CrateDependency {
1110 name: "wasm-bindgen".to_string(),
1111 version: "0.2".to_string(),
1112 features: vec![],
1113 });
1114 }
1115
1116 let mut escape_checker = EscapeChecker::new(interner);
1117 escape_checker.check_program(&stmts).map_err(|e| {
1118 ParseError {
1119 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
1120 span: e.span,
1121 }
1122 })?;
1123
1124 let type_env = crate::analysis::check_program(&stmts, interner, &codegen_registry)
1125 .map_err(|e| ParseError {
1126 kind: e.to_parse_error_kind(interner),
1127 span: crate::token::Span::default(),
1128 })?;
1129 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, interner, &type_env);
1130
1131 let has_c = stmts.iter().any(|stmt| {
1133 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
1134 match export_target {
1135 None => true,
1136 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1137 }
1138 } else {
1139 false
1140 }
1141 });
1142
1143 let c_header = if has_c {
1144 Some(generate_c_header(&stmts, "module", interner, &codegen_registry))
1145 } else {
1146 None
1147 };
1148
1149 if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
1150 dependencies.push(CrateDependency {
1151 name: "serde_json".to_string(),
1152 version: "1".to_string(),
1153 features: vec![],
1154 });
1155 }
1156
1157 let python_bindings = if has_c {
1158 Some(generate_python_bindings(&stmts, "module", interner, &codegen_registry))
1159 } else {
1160 None
1161 };
1162
1163 let (typescript_bindings, typescript_types) = if has_c {
1164 let (js, dts) = generate_typescript_bindings(&stmts, "module", interner, &codegen_registry);
1165 (Some(js), Some(dts))
1166 } else {
1167 (None, None)
1168 };
1169
1170 Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
1171}
1172
1173#[derive(Debug)]
1195pub enum CompileError {
1196 Parse(ParseError),
1201
1202 Io(String),
1206
1207 Build(String),
1212
1213 Runtime(String),
1217
1218 Ownership(LogosError),
1224}
1225
1226impl std::fmt::Display for CompileError {
1227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1228 match self {
1229 CompileError::Parse(e) => write!(f, "Parse error: {:?}", e),
1230 CompileError::Io(e) => write!(f, "IO error: {}", e),
1231 CompileError::Build(e) => write!(f, "Build error: {}", e),
1232 CompileError::Runtime(e) => write!(f, "Runtime error: {}", e),
1233 CompileError::Ownership(e) => write!(f, "{}", e),
1234 }
1235 }
1236}
1237
1238impl std::error::Error for CompileError {}
1239
1240pub fn encode_program_source(source: &str) -> Result<String, ParseError> {
1250 let full_source = if source.contains("## Main") || source.contains("## To ") {
1251 source.to_string()
1252 } else {
1253 format!("## Main\n{}", source)
1254 };
1255
1256 let mut interner = Interner::new();
1257 let mut lexer = Lexer::new(&full_source, &mut interner);
1258 let tokens = lexer.tokenize();
1259
1260 let type_registry = {
1261 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1262 let result = discovery.run_full();
1263 result.types
1264 };
1265
1266 let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
1268 for (_type_name, type_def) in type_registry.iter_types() {
1269 if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
1270 for variant in variants {
1271 let vname = interner.resolve(variant.name).to_string();
1272 let field_names: Vec<String> = variant.fields.iter()
1273 .map(|f| interner.resolve(f.name).to_string())
1274 .collect();
1275 variant_constructors.insert(vname, field_names);
1276 }
1277 }
1278 }
1279
1280 let mut world_state = WorldState::new();
1281 let expr_arena = Arena::new();
1282 let term_arena = Arena::new();
1283 let np_arena = Arena::new();
1284 let sym_arena = Arena::new();
1285 let role_arena = Arena::new();
1286 let pp_arena = Arena::new();
1287 let stmt_arena: Arena<Stmt> = Arena::new();
1288 let imperative_expr_arena: Arena<Expr> = Arena::new();
1289 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1290
1291 let ast_ctx = AstContext::with_types(
1292 &expr_arena, &term_arena, &np_arena, &sym_arena,
1293 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
1294 &type_expr_arena,
1295 );
1296
1297 let mut parser = crate::parser::Parser::new(
1298 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
1299 );
1300 let stmts = parser.parse_program()?;
1301
1302 let mut functions: Vec<(String, Vec<String>, Vec<&Stmt>)> = Vec::new();
1303 let mut main_stmts: Vec<&Stmt> = Vec::new();
1304
1305 for stmt in &stmts {
1306 if let Stmt::FunctionDef { name, params, body, .. } = stmt {
1307 let fn_name = interner.resolve(*name).to_string();
1308 let param_names: Vec<String> = params
1309 .iter()
1310 .map(|(name, _)| interner.resolve(*name).to_string())
1311 .collect();
1312 let body_stmts: Vec<&Stmt> = body.iter().collect();
1313 functions.push((fn_name, param_names, body_stmts));
1314 } else {
1315 main_stmts.push(stmt);
1316 }
1317 }
1318
1319 let mut counter = 0usize;
1320 let mut output = String::new();
1321
1322 output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1324
1325 for (fn_name, params, body) in &functions {
1326 let body_var = encode_stmt_list_src(body, &mut counter, &mut output, &interner, &variant_constructors);
1327
1328 let params_var = format!("params_{}", counter);
1329 counter += 1;
1330 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1331 for p in params {
1332 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1333 }
1334
1335 let func_var = format!("func_{}", counter);
1336 counter += 1;
1337 output.push_str(&format!(
1338 "Let {} be a new CFuncDef with name \"{}\" and params {} and body {}.\n",
1339 func_var, fn_name, params_var, body_var
1340 ));
1341 output.push_str(&format!(
1342 "Set item \"{}\" of encodedFuncMap to {}.\n",
1343 fn_name, func_var
1344 ));
1345 }
1346
1347 let main_var = encode_stmt_list_src(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1349 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1350
1351 Ok(output)
1352}
1353
1354fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1355 let mut free = HashSet::new();
1356 match expr {
1357 Expr::Identifier(sym) => {
1358 let name = interner.resolve(*sym).to_string();
1359 if !bound.contains(&name) {
1360 free.insert(name);
1361 }
1362 }
1363 Expr::BinaryOp { left, right, .. } => {
1364 free.extend(collect_free_vars_expr(left, interner, bound));
1365 free.extend(collect_free_vars_expr(right, interner, bound));
1366 }
1367 Expr::Not { operand } => {
1368 free.extend(collect_free_vars_expr(operand, interner, bound));
1369 }
1370 Expr::Copy { expr: inner } => {
1371 free.extend(collect_free_vars_expr(inner, interner, bound));
1372 }
1373 Expr::CallExpr { callee, args } => {
1374 free.extend(collect_free_vars_expr(callee, interner, bound));
1375 for a in args {
1376 free.extend(collect_free_vars_expr(a, interner, bound));
1377 }
1378 }
1379 Expr::Index { collection, index } => {
1380 free.extend(collect_free_vars_expr(collection, interner, bound));
1381 free.extend(collect_free_vars_expr(index, interner, bound));
1382 }
1383 Expr::InterpolatedString(parts) => {
1384 for part in parts {
1385 if let StringPart::Expr { value, .. } = part {
1386 free.extend(collect_free_vars_expr(value, interner, bound));
1387 }
1388 }
1389 }
1390 Expr::Closure { params, body, .. } => {
1391 let mut inner_bound = bound.clone();
1392 for (sym, _) in params {
1393 inner_bound.insert(interner.resolve(*sym).to_string());
1394 }
1395 match body {
1396 ClosureBody::Expression(e) => {
1397 free.extend(collect_free_vars_expr(e, interner, &inner_bound));
1398 }
1399 ClosureBody::Block(stmts) => {
1400 for s in stmts.iter() {
1401 free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
1402 }
1403 }
1404 }
1405 }
1406 _ => {}
1407 }
1408 free
1409}
1410
1411fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1412 let mut free = HashSet::new();
1413 match stmt {
1414 Stmt::Let { var, value, .. } => {
1415 free.extend(collect_free_vars_expr(value, interner, bound));
1416 }
1417 Stmt::Set { target, value, .. } => {
1418 let n = interner.resolve(*target).to_string();
1419 if !bound.contains(&n) {
1420 free.insert(n);
1421 }
1422 free.extend(collect_free_vars_expr(value, interner, bound));
1423 }
1424 Stmt::Show { object, .. } => {
1425 free.extend(collect_free_vars_expr(object, interner, bound));
1426 }
1427 Stmt::Return { value } => {
1428 if let Some(v) = value {
1429 free.extend(collect_free_vars_expr(v, interner, bound));
1430 }
1431 }
1432 _ => {}
1433 }
1434 free
1435}
1436
1437fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1438 let var = format!("e_{}", *counter);
1439 *counter += 1;
1440
1441 match expr {
1442 Expr::Literal(lit) => match lit {
1443 Literal::Number(n) => {
1444 output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
1445 }
1446 Literal::Boolean(b) => {
1447 output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
1448 }
1449 Literal::Text(s) => {
1450 let text = interner.resolve(*s);
1451 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
1452 }
1453 Literal::Float(f) => {
1454 output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, f));
1455 }
1456 Literal::Duration(nanos) => {
1457 let millis = nanos / 1_000_000;
1458 let amount_var = format!("e_{}", *counter);
1459 *counter += 1;
1460 output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
1461 output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
1462 }
1463 Literal::Nothing => {
1464 output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
1465 }
1466 _ => {
1467 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
1468 }
1469 },
1470 Expr::Identifier(sym) => {
1471 let name = interner.resolve(*sym);
1472 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
1473 }
1474 Expr::BinaryOp { op, left, right } => {
1475 let left_var = encode_expr_src(left, counter, output, interner, variants);
1476 let right_var = encode_expr_src(right, counter, output, interner, variants);
1477 let op_str = match op {
1478 BinaryOpKind::Add => "+",
1479 BinaryOpKind::Subtract => "-",
1480 BinaryOpKind::Multiply => "*",
1481 BinaryOpKind::Divide => "/",
1482 BinaryOpKind::Modulo => "%",
1483 BinaryOpKind::Eq => "==",
1484 BinaryOpKind::NotEq => "!=",
1485 BinaryOpKind::Lt => "<",
1486 BinaryOpKind::Gt => ">",
1487 BinaryOpKind::LtEq => "<=",
1488 BinaryOpKind::GtEq => ">=",
1489 BinaryOpKind::And => "&&",
1490 BinaryOpKind::Or => "||",
1491 BinaryOpKind::Concat => "+",
1492 BinaryOpKind::BitXor => "^",
1493 BinaryOpKind::Shl => "<<",
1494 BinaryOpKind::Shr => ">>",
1495 };
1496 output.push_str(&format!(
1497 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
1498 var, op_str, left_var, right_var
1499 ));
1500 }
1501 Expr::Not { operand } => {
1502 let inner_var = encode_expr_src(operand, counter, output, interner, variants);
1503 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
1504 }
1505 Expr::Call { function, args } => {
1506 let fn_name = interner.resolve(*function);
1507 if let Some(field_names) = variants.get(fn_name) {
1508 let names_var = format!("nvNames_{}", *counter);
1510 *counter += 1;
1511 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1512 let vals_var = format!("nvVals_{}", *counter);
1513 *counter += 1;
1514 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1515 for (i, arg) in args.iter().enumerate() {
1516 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
1517 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1518 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
1519 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
1520 }
1521 output.push_str(&format!(
1522 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1523 var, fn_name, names_var, vals_var
1524 ));
1525 } else {
1526 let args_var = format!("callArgs_{}", *counter);
1527 *counter += 1;
1528 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1529 for arg in args {
1530 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
1531 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1532 }
1533 output.push_str(&format!(
1534 "Let {} be a new CCall with name \"{}\" and args {}.\n",
1535 var, fn_name, args_var
1536 ));
1537 }
1538 }
1539 Expr::Index { collection, index } => {
1540 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1541 let idx_var = encode_expr_src(index, counter, output, interner, variants);
1542 output.push_str(&format!(
1543 "Let {} be a new CIndex with coll {} and idx {}.\n",
1544 var, coll_var, idx_var
1545 ));
1546 }
1547 Expr::Length { collection } => {
1548 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1549 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
1550 }
1551 Expr::FieldAccess { object, field } => {
1552 let obj_var = encode_expr_src(object, counter, output, interner, variants);
1553 let field_name = interner.resolve(*field);
1554 let key_var = format!("e_{}", *counter);
1555 *counter += 1;
1556 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
1557 output.push_str(&format!(
1558 "Let {} be a new CMapGet with target {} and key {}.\n",
1559 var, obj_var, key_var
1560 ));
1561 }
1562 Expr::NewVariant { variant, fields, .. } => {
1563 let variant_name = interner.resolve(*variant);
1564 let names_var = format!("nvNames_{}", *counter);
1565 *counter += 1;
1566 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1567 let vals_var = format!("nvVals_{}", *counter);
1568 *counter += 1;
1569 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1570 for (field_name, field_expr) in fields {
1571 let fname = interner.resolve(*field_name);
1572 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1573 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
1574 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1575 }
1576 output.push_str(&format!(
1577 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1578 var, variant_name, names_var, vals_var
1579 ));
1580 }
1581 Expr::New { type_name, init_fields, .. } => {
1582 let tn = interner.resolve(*type_name);
1583 if tn == "Seq" || tn == "List" {
1584 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
1585 } else if tn == "Set" {
1586 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
1587 } else if init_fields.is_empty() {
1588 let names_var = format!("nvNames_{}", *counter);
1589 *counter += 1;
1590 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1591 let vals_var = format!("nvVals_{}", *counter);
1592 *counter += 1;
1593 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1594 output.push_str(&format!(
1595 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1596 var, tn, names_var, vals_var
1597 ));
1598 } else {
1599 let names_var = format!("nvNames_{}", *counter);
1600 *counter += 1;
1601 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1602 let vals_var = format!("nvVals_{}", *counter);
1603 *counter += 1;
1604 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1605 for (field_name, field_expr) in init_fields {
1606 let fname = interner.resolve(*field_name);
1607 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1608 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
1609 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1610 }
1611 output.push_str(&format!(
1612 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1613 var, tn, names_var, vals_var
1614 ));
1615 }
1616 }
1617 Expr::InterpolatedString(parts) => {
1618 if parts.is_empty() {
1619 output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
1620 } else {
1621 let mut part_vars: Vec<String> = Vec::new();
1622 for part in parts {
1623 match part {
1624 StringPart::Literal(sym) => {
1625 let text = interner.resolve(*sym);
1626 let pv = format!("e_{}", *counter);
1627 *counter += 1;
1628 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", pv, text));
1629 part_vars.push(pv);
1630 }
1631 StringPart::Expr { value, .. } => {
1632 let pv = encode_expr_src(value, counter, output, interner, variants);
1633 part_vars.push(pv);
1634 }
1635 }
1636 }
1637 if part_vars.len() == 1 {
1638 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
1639 } else {
1640 let mut acc = part_vars[0].clone();
1641 for pv in &part_vars[1..] {
1642 let concat_var = format!("e_{}", *counter);
1643 *counter += 1;
1644 output.push_str(&format!(
1645 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
1646 concat_var, acc, pv
1647 ));
1648 acc = concat_var;
1649 }
1650 output.push_str(&format!("Let {} be {}.\n", var, acc));
1651 }
1652 }
1653 }
1654 Expr::Range { start, end } => {
1655 let start_var = encode_expr_src(start, counter, output, interner, variants);
1656 let end_var = encode_expr_src(end, counter, output, interner, variants);
1657 output.push_str(&format!(
1658 "Let {} be a new CRange with start {} and end {}.\n",
1659 var, start_var, end_var
1660 ));
1661 }
1662 Expr::Slice { collection, start, end } => {
1663 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1664 let start_var = encode_expr_src(start, counter, output, interner, variants);
1665 let end_var = encode_expr_src(end, counter, output, interner, variants);
1666 output.push_str(&format!(
1667 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
1668 var, coll_var, start_var, end_var
1669 ));
1670 }
1671 Expr::Copy { expr } => {
1672 let inner_var = encode_expr_src(expr, counter, output, interner, variants);
1673 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
1674 }
1675 Expr::Contains { collection, value } => {
1676 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1677 let val_var = encode_expr_src(value, counter, output, interner, variants);
1678 output.push_str(&format!(
1679 "Let {} be a new CContains with coll {} and elem {}.\n",
1680 var, coll_var, val_var
1681 ));
1682 }
1683 Expr::Union { left, right } => {
1684 let left_var = encode_expr_src(left, counter, output, interner, variants);
1685 let right_var = encode_expr_src(right, counter, output, interner, variants);
1686 output.push_str(&format!(
1687 "Let {} be a new CUnion with left {} and right {}.\n",
1688 var, left_var, right_var
1689 ));
1690 }
1691 Expr::Intersection { left, right } => {
1692 let left_var = encode_expr_src(left, counter, output, interner, variants);
1693 let right_var = encode_expr_src(right, counter, output, interner, variants);
1694 output.push_str(&format!(
1695 "Let {} be a new CIntersection with left {} and right {}.\n",
1696 var, left_var, right_var
1697 ));
1698 }
1699 Expr::OptionSome { value } => {
1700 let inner_var = encode_expr_src(value, counter, output, interner, variants);
1701 output.push_str(&format!(
1702 "Let {} be a new COptionSome with inner {}.\n",
1703 var, inner_var
1704 ));
1705 }
1706 Expr::OptionNone => {
1707 output.push_str(&format!("Let {} be a new COptionNone.\n", var));
1708 }
1709 Expr::Tuple(elems) => {
1710 let items_var = format!("tupItems_{}", *counter);
1711 *counter += 1;
1712 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
1713 for elem in elems {
1714 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
1715 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
1716 }
1717 output.push_str(&format!(
1718 "Let {} be a new CTuple with items {}.\n",
1719 var, items_var
1720 ));
1721 }
1722 Expr::Closure { params, body, .. } => {
1723 let params_var = format!("clp_{}", *counter);
1724 *counter += 1;
1725 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1726 let mut param_names = HashSet::new();
1727 for (sym, _) in params {
1728 let name = interner.resolve(*sym);
1729 param_names.insert(name.to_string());
1730 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
1731 }
1732 let body_var = format!("clb_{}", *counter);
1733 *counter += 1;
1734 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
1735 match body {
1736 ClosureBody::Expression(e) => {
1737 let ret_expr = encode_expr_src(e, counter, output, interner, variants);
1738 let ret_var = format!("s_{}", *counter);
1739 *counter += 1;
1740 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
1741 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
1742 }
1743 ClosureBody::Block(stmts) => {
1744 for s in stmts.iter() {
1745 let sv = encode_stmt_src(s, counter, output, interner, variants);
1746 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
1747 }
1748 }
1749 }
1750 let bound: HashSet<String> = param_names;
1751 let free = collect_free_vars_expr(expr, interner, &bound);
1752 let cap_var = format!("clc_{}", *counter);
1753 *counter += 1;
1754 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
1755 for fv in &free {
1756 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
1757 }
1758 output.push_str(&format!(
1759 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
1760 var, params_var, body_var, cap_var
1761 ));
1762 }
1763 Expr::CallExpr { callee, args } => {
1764 let callee_var = encode_expr_src(callee, counter, output, interner, variants);
1765 let args_var = format!("cea_{}", *counter);
1766 *counter += 1;
1767 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1768 for a in args {
1769 let av = encode_expr_src(a, counter, output, interner, variants);
1770 output.push_str(&format!("Push {} to {}.\n", av, args_var));
1771 }
1772 output.push_str(&format!(
1773 "Let {} be a new CCallExpr with target {} and args {}.\n",
1774 var, callee_var, args_var
1775 ));
1776 }
1777 Expr::Give { value } => {
1778 let inner_var = encode_expr_src(value, counter, output, interner, variants);
1779 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
1780 }
1781 Expr::Escape { code, .. } => {
1782 let code_str = interner.resolve(*code);
1783 output.push_str(&format!(
1784 "Let {} be a new CEscExpr with code \"{}\".\n",
1785 var, code_str.replace('\"', "\\\"")
1786 ));
1787 }
1788 _ => {
1789 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
1790 }
1791 }
1792
1793 var
1794}
1795
1796fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1797 let var = format!("s_{}", *counter);
1798 *counter += 1;
1799
1800 match stmt {
1801 Stmt::Let { var: name, value, .. } => {
1802 let name_str = interner.resolve(*name);
1803 let expr_var = encode_expr_src(value, counter, output, interner, variants);
1804 output.push_str(&format!(
1805 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
1806 var, name_str, expr_var
1807 ));
1808 }
1809 Stmt::Set { target, value } => {
1810 let name_str = interner.resolve(*target);
1811 let expr_var = encode_expr_src(value, counter, output, interner, variants);
1812 output.push_str(&format!(
1813 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
1814 var, name_str, expr_var
1815 ));
1816 }
1817 Stmt::If { cond, then_block, else_block } => {
1818 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
1819 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
1820 let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
1821 let else_var = if let Some(els) = else_block {
1822 let else_stmts: Vec<&Stmt> = els.iter().collect();
1823 encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
1824 } else {
1825 let empty_var = format!("emptyBlock_{}", *counter);
1826 *counter += 1;
1827 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
1828 empty_var
1829 };
1830 output.push_str(&format!(
1831 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
1832 var, cond_var, then_var, else_var
1833 ));
1834 }
1835 Stmt::While { cond, body, .. } => {
1836 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
1837 let body_stmts: Vec<&Stmt> = body.iter().collect();
1838 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
1839 output.push_str(&format!(
1840 "Let {} be a new CWhile with cond {} and body {}.\n",
1841 var, cond_var, body_var
1842 ));
1843 }
1844 Stmt::Return { value } => {
1845 if let Some(expr) = value {
1846 let expr_var = encode_expr_src(expr, counter, output, interner, variants);
1847 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
1848 } else {
1849 let nothing_var = format!("e_{}", *counter);
1850 *counter += 1;
1851 output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
1852 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
1853 }
1854 }
1855 Stmt::Show { object, .. } => {
1856 let expr_var = encode_expr_src(object, counter, output, interner, variants);
1857 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
1858 }
1859 Stmt::Call { function, args } => {
1860 let fn_name = interner.resolve(*function);
1861 let args_var = format!("callSArgs_{}", *counter);
1862 *counter += 1;
1863 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1864 for arg in args {
1865 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
1866 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1867 }
1868 output.push_str(&format!(
1869 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
1870 var, fn_name, args_var
1871 ));
1872 }
1873 Stmt::Push { value, collection } => {
1874 let val_var = encode_expr_src(value, counter, output, interner, variants);
1875 let coll_name = extract_ident_name(collection, interner);
1876 output.push_str(&format!(
1877 "Let {} be a new CPush with expr {} and target \"{}\".\n",
1878 var, val_var, coll_name
1879 ));
1880 }
1881 Stmt::SetIndex { collection, index, value } => {
1882 let coll_name = extract_ident_name(collection, interner);
1883 let idx_var = encode_expr_src(index, counter, output, interner, variants);
1884 let val_var = encode_expr_src(value, counter, output, interner, variants);
1885 output.push_str(&format!(
1886 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
1887 var, coll_name, idx_var, val_var
1888 ));
1889 }
1890 Stmt::SetField { object, field, value } => {
1891 let map_name = extract_ident_name(object, interner);
1892 let field_name = interner.resolve(*field);
1893 let key_var = format!("e_{}", *counter);
1894 *counter += 1;
1895 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
1896 let val_var = encode_expr_src(value, counter, output, interner, variants);
1897 output.push_str(&format!(
1898 "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
1899 var, map_name, key_var, val_var
1900 ));
1901 }
1902 Stmt::Pop { collection, .. } => {
1903 let coll_name = extract_ident_name(collection, interner);
1904 output.push_str(&format!(
1905 "Let {} be a new CPop with target \"{}\".\n",
1906 var, coll_name
1907 ));
1908 }
1909 Stmt::Add { value, collection } => {
1910 let val_var = encode_expr_src(value, counter, output, interner, variants);
1911 let coll_name = extract_ident_name(collection, interner);
1912 output.push_str(&format!(
1913 "Let {} be a new CAdd with elem {} and target \"{}\".\n",
1914 var, val_var, coll_name
1915 ));
1916 }
1917 Stmt::Remove { value, collection } => {
1918 let val_var = encode_expr_src(value, counter, output, interner, variants);
1919 let coll_name = extract_ident_name(collection, interner);
1920 output.push_str(&format!(
1921 "Let {} be a new CRemove with elem {} and target \"{}\".\n",
1922 var, val_var, coll_name
1923 ));
1924 }
1925 Stmt::Inspect { .. } => {
1926 return String::new(); }
1928 Stmt::Repeat { .. } => {
1929 return String::new(); }
1931 Stmt::Break => {
1932 output.push_str(&format!("Let {} be a new CBreak.\n", var));
1933 }
1934 Stmt::RuntimeAssert { condition, .. } => {
1935 let cond_var = encode_expr_src(condition, counter, output, interner, variants);
1936 let msg_var = format!("e_{}", *counter);
1937 *counter += 1;
1938 output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
1939 output.push_str(&format!(
1940 "Let {} be a new CRuntimeAssert with cond {} and msg {}.\n",
1941 var, cond_var, msg_var
1942 ));
1943 }
1944 Stmt::Give { object, recipient } => {
1945 let expr_var = encode_expr_src(object, counter, output, interner, variants);
1946 let target_name = extract_ident_name(recipient, interner);
1947 output.push_str(&format!(
1948 "Let {} be a new CGive with expr {} and target \"{}\".\n",
1949 var, expr_var, target_name
1950 ));
1951 }
1952 Stmt::Escape { code, .. } => {
1953 let code_str = interner.resolve(*code);
1954 output.push_str(&format!(
1955 "Let {} be a new CEscStmt with code \"{}\".\n",
1956 var, code_str.replace('\"', "\\\"")
1957 ));
1958 }
1959 Stmt::Sleep { milliseconds } => {
1960 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
1961 output.push_str(&format!(
1962 "Let {} be a new CSleep with duration {}.\n",
1963 var, dur_var
1964 ));
1965 }
1966 Stmt::ReadFrom { var: read_var, source } => {
1967 let var_name = interner.resolve(*read_var);
1968 match source {
1969 ReadSource::Console => {
1970 output.push_str(&format!(
1971 "Let {} be a new CReadConsole with target \"{}\".\n",
1972 var, var_name
1973 ));
1974 }
1975 ReadSource::File(path_expr) => {
1976 let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
1977 output.push_str(&format!(
1978 "Let {} be a new CReadFile with path {} and target \"{}\".\n",
1979 var, path_var, var_name
1980 ));
1981 }
1982 }
1983 }
1984 Stmt::WriteFile { content, path } => {
1985 let path_var = encode_expr_src(path, counter, output, interner, variants);
1986 let content_var = encode_expr_src(content, counter, output, interner, variants);
1987 output.push_str(&format!(
1988 "Let {} be a new CWriteFile with path {} and content {}.\n",
1989 var, path_var, content_var
1990 ));
1991 }
1992 Stmt::Check { source_text, .. } => {
1993 let pred_var = format!("e_{}", *counter);
1994 *counter += 1;
1995 output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
1996 let msg_var = format!("e_{}", *counter);
1997 *counter += 1;
1998 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
1999 output.push_str(&format!(
2000 "Let {} be a new CCheck with predicate {} and msg {}.\n",
2001 var, pred_var, msg_var
2002 ));
2003 }
2004 Stmt::Assert { .. } => {
2005 let prop_var = format!("e_{}", *counter);
2006 *counter += 1;
2007 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2008 output.push_str(&format!(
2009 "Let {} be a new CAssert with proposition {}.\n",
2010 var, prop_var
2011 ));
2012 }
2013 Stmt::Trust { justification, .. } => {
2014 let prop_var = format!("e_{}", *counter);
2015 *counter += 1;
2016 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2017 let just_str = interner.resolve(*justification);
2018 output.push_str(&format!(
2019 "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
2020 var, prop_var, just_str
2021 ));
2022 }
2023 Stmt::Require { crate_name, .. } => {
2024 let dep_name = interner.resolve(*crate_name);
2025 output.push_str(&format!(
2026 "Let {} be a new CRequire with dependency \"{}\".\n",
2027 var, dep_name
2028 ));
2029 }
2030 Stmt::MergeCrdt { source, target } => {
2031 let source_var = encode_expr_src(source, counter, output, interner, variants);
2032 let target_name = match target {
2033 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2034 _ => "unknown".to_string(),
2035 };
2036 output.push_str(&format!(
2037 "Let {} be a new CMerge with target \"{}\" and other {}.\n",
2038 var, target_name, source_var
2039 ));
2040 }
2041 Stmt::IncreaseCrdt { object, field, amount } => {
2042 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2043 let target_name = match object {
2044 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2045 _ => "unknown".to_string(),
2046 };
2047 output.push_str(&format!(
2048 "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
2049 var, target_name, amount_var
2050 ));
2051 }
2052 Stmt::DecreaseCrdt { object, field, amount } => {
2053 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2054 let target_name = match object {
2055 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2056 _ => "unknown".to_string(),
2057 };
2058 output.push_str(&format!(
2059 "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
2060 var, target_name, amount_var
2061 ));
2062 }
2063 Stmt::AppendToSequence { sequence, value } => {
2064 let value_var = encode_expr_src(value, counter, output, interner, variants);
2065 let target_name = match sequence {
2066 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2067 _ => "unknown".to_string(),
2068 };
2069 output.push_str(&format!(
2070 "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
2071 var, target_name, value_var
2072 ));
2073 }
2074 Stmt::ResolveConflict { object, .. } => {
2075 let target_name = match object {
2076 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2077 _ => "unknown".to_string(),
2078 };
2079 output.push_str(&format!(
2080 "Let {} be a new CResolve with target \"{}\".\n",
2081 var, target_name
2082 ));
2083 }
2084 Stmt::Sync { var: sync_var, topic } => {
2085 let topic_var = encode_expr_src(topic, counter, output, interner, variants);
2086 let var_name = interner.resolve(*sync_var);
2087 output.push_str(&format!(
2088 "Let {} be a new CSync with target \"{}\" and channel {}.\n",
2089 var, var_name, topic_var
2090 ));
2091 }
2092 Stmt::Mount { var: mount_var, path } => {
2093 let path_var = encode_expr_src(path, counter, output, interner, variants);
2094 let var_name = interner.resolve(*mount_var);
2095 output.push_str(&format!(
2096 "Let {} be a new CMount with target \"{}\" and path {}.\n",
2097 var, var_name, path_var
2098 ));
2099 }
2100 Stmt::Concurrent { tasks } => {
2101 let branches_var = format!("e_{}", *counter);
2102 *counter += 1;
2103 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2104 let branch_var = format!("e_{}", *counter);
2105 *counter += 1;
2106 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2107 for stmt in tasks.iter() {
2108 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2109 if !sv.is_empty() {
2110 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2111 }
2112 }
2113 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2114 output.push_str(&format!(
2115 "Let {} be a new CConcurrent with branches {}.\n",
2116 var, branches_var
2117 ));
2118 }
2119 Stmt::Parallel { tasks } => {
2120 let branches_var = format!("e_{}", *counter);
2121 *counter += 1;
2122 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2123 let branch_var = format!("e_{}", *counter);
2124 *counter += 1;
2125 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2126 for stmt in tasks.iter() {
2127 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2128 if !sv.is_empty() {
2129 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2130 }
2131 }
2132 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2133 output.push_str(&format!(
2134 "Let {} be a new CParallel with branches {}.\n",
2135 var, branches_var
2136 ));
2137 }
2138 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
2139 let func_name = interner.resolve(*function);
2140 let args_var = format!("e_{}", *counter);
2141 *counter += 1;
2142 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2143 for arg in args {
2144 let av = encode_expr_src(arg, counter, output, interner, variants);
2145 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2146 }
2147 let body_var = format!("e_{}", *counter);
2148 *counter += 1;
2149 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2150 let call_var = format!("e_{}", *counter);
2151 *counter += 1;
2152 output.push_str(&format!(
2153 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2154 call_var, func_name, args_var
2155 ));
2156 output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
2157 let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
2158 interner.resolve(*handle).to_string()
2159 } else {
2160 "_task".to_string()
2161 };
2162 output.push_str(&format!(
2163 "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
2164 var, body_var, handle_name
2165 ));
2166 }
2167 Stmt::StopTask { handle } => {
2168 let handle_var = encode_expr_src(handle, counter, output, interner, variants);
2169 output.push_str(&format!(
2170 "Let {} be a new CStopTask with handle {}.\n",
2171 var, handle_var
2172 ));
2173 }
2174 Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
2175 let cap = capacity.unwrap_or(32);
2176 let cap_var = format!("e_{}", *counter);
2177 *counter += 1;
2178 output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
2179 let pipe_name = interner.resolve(*pipe_var);
2180 output.push_str(&format!(
2181 "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
2182 var, pipe_name, cap_var
2183 ));
2184 }
2185 Stmt::SendPipe { value, pipe } => {
2186 let val_var = encode_expr_src(value, counter, output, interner, variants);
2187 let pipe_name = match pipe {
2188 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2189 _ => "pipe".to_string(),
2190 };
2191 output.push_str(&format!(
2192 "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
2193 var, pipe_name, val_var
2194 ));
2195 }
2196 Stmt::ReceivePipe { var: recv_var, pipe } => {
2197 let recv_name = interner.resolve(*recv_var);
2198 let pipe_name = match pipe {
2199 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2200 _ => "pipe".to_string(),
2201 };
2202 output.push_str(&format!(
2203 "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
2204 var, pipe_name, recv_name
2205 ));
2206 }
2207 Stmt::TrySendPipe { value, pipe, .. } => {
2208 let val_var = encode_expr_src(value, counter, output, interner, variants);
2209 let pipe_name = match pipe {
2210 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2211 _ => "pipe".to_string(),
2212 };
2213 output.push_str(&format!(
2214 "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
2215 var, pipe_name, val_var
2216 ));
2217 }
2218 Stmt::TryReceivePipe { var: recv_var, pipe } => {
2219 let recv_name = interner.resolve(*recv_var);
2220 let pipe_name = match pipe {
2221 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2222 _ => "pipe".to_string(),
2223 };
2224 output.push_str(&format!(
2225 "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
2226 var, pipe_name, recv_name
2227 ));
2228 }
2229 Stmt::Select { branches } => {
2230 let branches_var = format!("e_{}", *counter);
2231 *counter += 1;
2232 output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
2233 for branch in branches {
2234 match branch {
2235 SelectBranch::Receive { var: recv_var, pipe, body } => {
2236 let recv_name = interner.resolve(*recv_var);
2237 let pipe_name = match pipe {
2238 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2239 _ => "pipe".to_string(),
2240 };
2241 let body_var = format!("e_{}", *counter);
2242 *counter += 1;
2243 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2244 for stmt in body.iter() {
2245 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2246 if !sv.is_empty() {
2247 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2248 }
2249 }
2250 let branch_var = format!("e_{}", *counter);
2251 *counter += 1;
2252 output.push_str(&format!(
2253 "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
2254 branch_var, pipe_name, recv_name, body_var
2255 ));
2256 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2257 }
2258 SelectBranch::Timeout { milliseconds, body } => {
2259 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2260 let body_var = format!("e_{}", *counter);
2261 *counter += 1;
2262 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2263 for stmt in body.iter() {
2264 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2265 if !sv.is_empty() {
2266 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2267 }
2268 }
2269 let branch_var = format!("e_{}", *counter);
2270 *counter += 1;
2271 output.push_str(&format!(
2272 "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
2273 branch_var, dur_var, body_var
2274 ));
2275 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2276 }
2277 }
2278 }
2279 output.push_str(&format!(
2280 "Let {} be a new CSelect with branches {}.\n",
2281 var, branches_var
2282 ));
2283 }
2284 Stmt::Spawn { agent_type, name } => {
2285 let agent_name = interner.resolve(*agent_type);
2286 let target_name = interner.resolve(*name);
2287 output.push_str(&format!(
2288 "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
2289 var, agent_name, target_name
2290 ));
2291 }
2292 Stmt::SendMessage { message, destination } => {
2293 let target_var = encode_expr_src(destination, counter, output, interner, variants);
2294 let msg_var = encode_expr_src(message, counter, output, interner, variants);
2295 output.push_str(&format!(
2296 "Let {} be a new CSendMessage with target {} and msg {}.\n",
2297 var, target_var, msg_var
2298 ));
2299 }
2300 Stmt::AwaitMessage { into, .. } => {
2301 let await_name = interner.resolve(*into);
2302 output.push_str(&format!(
2303 "Let {} be a new CAwaitMessage with target \"{}\".\n",
2304 var, await_name
2305 ));
2306 }
2307 Stmt::Listen { address } => {
2308 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2309 output.push_str(&format!(
2310 "Let {} be a new CListen with addr {} and handler \"default\".\n",
2311 var, addr_var
2312 ));
2313 }
2314 Stmt::ConnectTo { address } => {
2315 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2316 output.push_str(&format!(
2317 "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
2318 var, addr_var
2319 ));
2320 }
2321 Stmt::Zone { name, body, .. } => {
2322 let zone_name = interner.resolve(*name);
2323 let body_var = format!("e_{}", *counter);
2324 *counter += 1;
2325 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2326 for stmt in body.iter() {
2327 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2328 if !sv.is_empty() {
2329 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2330 }
2331 }
2332 output.push_str(&format!(
2333 "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
2334 var, zone_name, body_var
2335 ));
2336 }
2337 Stmt::LetPeerAgent { var: pa_var, address } => {
2338 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2339 let pa_name = interner.resolve(*pa_var);
2340 output.push_str(&format!(
2341 "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
2342 var, addr_var, pa_name
2343 ));
2344 }
2345 _ => {
2346 return String::new();
2347 }
2348 }
2349
2350 var
2351}
2352
2353fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
2354 match stmt {
2355 Stmt::Inspect { target, arms, .. } => {
2356 let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
2357 let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
2358
2359 for arm in arms {
2360 if arm.variant.is_none() {
2361 otherwise_stmts = arm.body.iter().collect();
2362 } else {
2363 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
2364 variant_arms.push((arm, body_refs));
2365 }
2366 }
2367
2368 if variant_arms.is_empty() {
2369 let mut result = Vec::new();
2370 for s in &otherwise_stmts {
2371 for v in encode_stmts_src(s, counter, output, interner, variants) {
2372 result.push(v);
2373 }
2374 }
2375 return result;
2376 }
2377
2378 let has_otherwise = !otherwise_stmts.is_empty();
2383 let mut result = Vec::new();
2384
2385 let matched_var_name = if has_otherwise {
2387 let name = format!("__inspectMatched_{}", *counter);
2388 *counter += 1;
2389 let false_expr = format!("e_{}", *counter);
2390 *counter += 1;
2391 output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
2392 let let_stmt = format!("s_{}", *counter);
2393 *counter += 1;
2394 output.push_str(&format!(
2395 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2396 let_stmt, name, false_expr
2397 ));
2398 result.push(let_stmt);
2399 Some(name)
2400 } else {
2401 None
2402 };
2403
2404 for (arm, body_stmts) in &variant_arms {
2406 let variant_name = interner.resolve(arm.variant.unwrap());
2407
2408 let tag_target = encode_expr_src(target, counter, output, interner, variants);
2410 let tag_key = format!("e_{}", *counter);
2411 *counter += 1;
2412 output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
2413 let tag_get = format!("e_{}", *counter);
2414 *counter += 1;
2415 output.push_str(&format!(
2416 "Let {} be a new CMapGet with target {} and key {}.\n",
2417 tag_get, tag_target, tag_key
2418 ));
2419 let variant_text = format!("e_{}", *counter);
2420 *counter += 1;
2421 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
2422 let cond_var = format!("e_{}", *counter);
2423 *counter += 1;
2424 output.push_str(&format!(
2425 "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
2426 cond_var, tag_get, variant_text
2427 ));
2428
2429 let then_list = format!("stmtList_{}", *counter);
2431 *counter += 1;
2432 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
2433
2434 if let Some(ref mname) = matched_var_name {
2436 let true_expr = format!("e_{}", *counter);
2437 *counter += 1;
2438 output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
2439 let set_stmt = format!("s_{}", *counter);
2440 *counter += 1;
2441 output.push_str(&format!(
2442 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2443 set_stmt, mname, true_expr
2444 ));
2445 output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
2446 }
2447
2448 for (field_name, binding_name) in &arm.bindings {
2450 let field_str = interner.resolve(*field_name);
2451 let bind_str = interner.resolve(*binding_name);
2452 let bind_target = encode_expr_src(target, counter, output, interner, variants);
2453 let fkey = format!("e_{}", *counter);
2454 *counter += 1;
2455 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
2456 let fget = format!("e_{}", *counter);
2457 *counter += 1;
2458 output.push_str(&format!(
2459 "Let {} be a new CMapGet with target {} and key {}.\n",
2460 fget, bind_target, fkey
2461 ));
2462 let bind_let = format!("s_{}", *counter);
2463 *counter += 1;
2464 output.push_str(&format!(
2465 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2466 bind_let, bind_str, fget
2467 ));
2468 output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
2469 }
2470
2471 for body_stmt in body_stmts {
2473 match body_stmt {
2474 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
2475 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
2476 for v in vars {
2477 output.push_str(&format!("Push {} to {}.\n", v, then_list));
2478 }
2479 }
2480 _ => {
2481 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
2482 if !bvar.is_empty() {
2483 output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
2484 }
2485 }
2486 }
2487 }
2488
2489 let empty_else = format!("stmtList_{}", *counter);
2491 *counter += 1;
2492 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
2493
2494 let if_var = format!("s_{}", *counter);
2496 *counter += 1;
2497 output.push_str(&format!(
2498 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2499 if_var, cond_var, then_list, empty_else
2500 ));
2501
2502 result.push(if_var);
2503 }
2504
2505 if let Some(ref mname) = matched_var_name {
2507 let matched_ref = format!("e_{}", *counter);
2508 *counter += 1;
2509 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
2510 let not_matched = format!("e_{}", *counter);
2511 *counter += 1;
2512 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
2513
2514 let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
2515 let empty_else = format!("stmtList_{}", *counter);
2516 *counter += 1;
2517 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
2518
2519 let otherwise_if = format!("s_{}", *counter);
2520 *counter += 1;
2521 output.push_str(&format!(
2522 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2523 otherwise_if, not_matched, otherwise_block, empty_else
2524 ));
2525 result.push(otherwise_if);
2526 }
2527
2528 result
2529 }
2530 Stmt::Repeat { pattern, iterable, body, .. } => {
2531 let loop_var_name = match pattern {
2533 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
2534 Pattern::Tuple(syms) => {
2535 if let Some(s) = syms.first() {
2536 interner.resolve(*s).to_string()
2537 } else {
2538 "item".to_string()
2539 }
2540 }
2541 };
2542
2543 let idx_name = format!("__idx_{}", *counter);
2545 *counter += 1;
2546
2547 let idx_init_expr = format!("e_{}", *counter);
2549 *counter += 1;
2550 output.push_str(&format!("Let {} be a new CInt with value 1.\n", idx_init_expr));
2551 let idx_init = format!("s_{}", *counter);
2552 *counter += 1;
2553 output.push_str(&format!(
2554 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2555 idx_init, idx_name, idx_init_expr
2556 ));
2557
2558 let iter_for_len = encode_expr_src(iterable, counter, output, interner, variants);
2560 let idx_var_expr = format!("e_{}", *counter);
2561 *counter += 1;
2562 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_var_expr, idx_name));
2563 let len_expr = format!("e_{}", *counter);
2564 *counter += 1;
2565 output.push_str(&format!("Let {} be a new CLen with target {}.\n", len_expr, iter_for_len));
2566 let while_cond = format!("e_{}", *counter);
2567 *counter += 1;
2568 output.push_str(&format!(
2569 "Let {} be a new CBinOp with op \"<=\" and left {} and right {}.\n",
2570 while_cond, idx_var_expr, len_expr
2571 ));
2572
2573 let while_body_list = format!("stmtList_{}", *counter);
2575 *counter += 1;
2576 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", while_body_list));
2577
2578 let iter_for_index = encode_expr_src(iterable, counter, output, interner, variants);
2580 let idx_ref = format!("e_{}", *counter);
2581 *counter += 1;
2582 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref, idx_name));
2583 let elem_expr = format!("e_{}", *counter);
2584 *counter += 1;
2585 output.push_str(&format!(
2586 "Let {} be a new CIndex with coll {} and idx {}.\n",
2587 elem_expr, iter_for_index, idx_ref
2588 ));
2589 let elem_let = format!("s_{}", *counter);
2590 *counter += 1;
2591 output.push_str(&format!(
2592 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2593 elem_let, loop_var_name, elem_expr
2594 ));
2595 output.push_str(&format!("Push {} to {}.\n", elem_let, while_body_list));
2596
2597 let body_refs: Vec<&Stmt> = body.iter().collect();
2599 for body_stmt in &body_refs {
2600 match body_stmt {
2601 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
2602 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
2603 for v in vars {
2604 output.push_str(&format!("Push {} to {}.\n", v, while_body_list));
2605 }
2606 }
2607 _ => {
2608 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
2609 if !bvar.is_empty() {
2610 output.push_str(&format!("Push {} to {}.\n", bvar, while_body_list));
2611 }
2612 }
2613 }
2614 }
2615
2616 let idx_ref2 = format!("e_{}", *counter);
2618 *counter += 1;
2619 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref2, idx_name));
2620 let one_expr = format!("e_{}", *counter);
2621 *counter += 1;
2622 output.push_str(&format!("Let {} be a new CInt with value 1.\n", one_expr));
2623 let inc_expr = format!("e_{}", *counter);
2624 *counter += 1;
2625 output.push_str(&format!(
2626 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
2627 inc_expr, idx_ref2, one_expr
2628 ));
2629 let inc_set = format!("s_{}", *counter);
2630 *counter += 1;
2631 output.push_str(&format!(
2632 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2633 inc_set, idx_name, inc_expr
2634 ));
2635 output.push_str(&format!("Push {} to {}.\n", inc_set, while_body_list));
2636
2637 let while_var = format!("s_{}", *counter);
2639 *counter += 1;
2640 output.push_str(&format!(
2641 "Let {} be a new CWhile with cond {} and body {}.\n",
2642 while_var, while_cond, while_body_list
2643 ));
2644
2645 vec![idx_init, while_var]
2646 }
2647 _ => {
2648 let v = encode_stmt_src(stmt, counter, output, interner, variants);
2649 if v.is_empty() {
2650 vec![]
2651 } else {
2652 vec![v]
2653 }
2654 }
2655 }
2656}
2657
2658fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2659 let list_var = format!("stmtList_{}", *counter);
2660 *counter += 1;
2661 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
2662
2663 for stmt in stmts {
2664 for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
2665 output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
2666 }
2667 }
2668
2669 list_var
2670}
2671
2672fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
2673 match expr {
2674 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2675 _ => "unknown".to_string(),
2676 }
2677}
2678
2679pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
2692 let full_source = if program.contains("## Main") || program.contains("## To ") {
2693 program.to_string()
2694 } else {
2695 format!("## Main\n{}", program)
2696 };
2697
2698 let mut interner = Interner::new();
2699 let mut lexer = Lexer::new(&full_source, &mut interner);
2700 let tokens = lexer.tokenize();
2701
2702 let type_registry = {
2703 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
2704 let result = discovery.run_full();
2705 result.types
2706 };
2707
2708 let mut world_state = WorldState::new();
2709 let expr_arena = Arena::new();
2710 let term_arena = Arena::new();
2711 let np_arena = Arena::new();
2712 let sym_arena = Arena::new();
2713 let role_arena = Arena::new();
2714 let pp_arena = Arena::new();
2715 let stmt_arena: Arena<Stmt> = Arena::new();
2716 let imperative_expr_arena: Arena<Expr> = Arena::new();
2717 let type_expr_arena: Arena<TypeExpr> = Arena::new();
2718
2719 let ast_ctx = AstContext::with_types(
2720 &expr_arena, &term_arena, &np_arena, &sym_arena,
2721 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
2722 &type_expr_arena,
2723 );
2724
2725 let mut parser = crate::parser::Parser::new(
2726 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
2727 );
2728 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
2729
2730 let optimized = crate::optimize::optimize_for_projection(
2735 stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
2736 );
2737
2738 let mut output = String::new();
2739
2740 for stmt in &optimized {
2741 if matches!(stmt, Stmt::FunctionDef { .. }) {
2742 decompile_stmt(stmt, &interner, &mut output, 0);
2743 output.push('\n');
2744 }
2745 }
2746
2747 output.push_str("## Main\n");
2748 for stmt in &optimized {
2749 if !matches!(stmt, Stmt::FunctionDef { .. }) {
2750 decompile_stmt(stmt, &interner, &mut output, 0);
2751 }
2752 }
2753
2754 Ok(output)
2755}
2756
2757fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
2758 let pad = " ".repeat(indent);
2759 match stmt {
2760 Stmt::FunctionDef { name, params, body, return_type, .. } => {
2761 let fn_name = interner.resolve(*name);
2762 let param_strs: Vec<String> = params
2763 .iter()
2764 .map(|(name, ty)| {
2765 let pname = interner.resolve(*name);
2766 format!("{}: {}", pname, decompile_type_expr(ty, interner))
2767 })
2768 .collect();
2769 let ret_str = if let Some(rt) = return_type {
2770 format!(" -> {}", decompile_type_expr(rt, interner))
2771 } else {
2772 String::new()
2773 };
2774 out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
2775 for s in body.iter() {
2776 decompile_stmt(s, interner, out, indent + 1);
2777 }
2778 }
2779 Stmt::Let { var, value, mutable, .. } => {
2780 let name = interner.resolve(*var);
2781 let expr_str = decompile_expr(value, interner);
2782 if *mutable {
2783 out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
2784 } else {
2785 out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
2786 }
2787 }
2788 Stmt::Set { target, value } => {
2789 let name = interner.resolve(*target);
2790 let expr_str = decompile_expr(value, interner);
2791 out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
2792 }
2793 Stmt::Show { object, .. } => {
2794 let expr_str = decompile_expr(object, interner);
2795 out.push_str(&format!("{}Show {}.\n", pad, expr_str));
2796 }
2797 Stmt::Return { value } => {
2798 if let Some(expr) = value {
2799 let expr_str = decompile_expr(expr, interner);
2800 out.push_str(&format!("{}Return {}.\n", pad, expr_str));
2801 } else {
2802 out.push_str(&format!("{}Return.\n", pad));
2803 }
2804 }
2805 Stmt::If { cond, then_block, else_block } => {
2806 let cond_str = decompile_expr(cond, interner);
2807 out.push_str(&format!("{}If {}:\n", pad, cond_str));
2808 for s in then_block.iter() {
2809 decompile_stmt(s, interner, out, indent + 1);
2810 }
2811 if let Some(els) = else_block {
2812 out.push_str(&format!("{}Otherwise:\n", pad));
2813 for s in els.iter() {
2814 decompile_stmt(s, interner, out, indent + 1);
2815 }
2816 }
2817 }
2818 Stmt::While { cond, body, .. } => {
2819 let cond_str = decompile_expr(cond, interner);
2820 out.push_str(&format!("{}While {}:\n", pad, cond_str));
2821 for s in body.iter() {
2822 decompile_stmt(s, interner, out, indent + 1);
2823 }
2824 }
2825 Stmt::Call { function, args } => {
2826 let fn_name = interner.resolve(*function);
2827 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
2828 if arg_strs.is_empty() {
2829 out.push_str(&format!("{}{}().\n", pad, fn_name));
2830 } else {
2831 out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
2832 }
2833 }
2834 Stmt::Push { value, collection } => {
2835 let val_str = decompile_expr(value, interner);
2836 let coll_str = decompile_expr(collection, interner);
2837 out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
2838 }
2839 Stmt::SetIndex { collection, index, value } => {
2840 let coll_str = decompile_expr(collection, interner);
2841 let idx_str = decompile_expr(index, interner);
2842 let val_str = decompile_expr(value, interner);
2843 out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
2844 }
2845 Stmt::SetField { object, field, value } => {
2846 let obj_str = decompile_expr(object, interner);
2847 let field_name = interner.resolve(*field);
2848 let val_str = decompile_expr(value, interner);
2849 out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
2850 }
2851 Stmt::Repeat { pattern, iterable, body, .. } => {
2852 let var_name = match pattern {
2853 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
2854 Pattern::Tuple(syms) => {
2855 syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
2856 }
2857 };
2858 let iter_str = decompile_expr(iterable, interner);
2859 out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
2860 for s in body.iter() {
2861 decompile_stmt(s, interner, out, indent + 1);
2862 }
2863 }
2864 Stmt::Inspect { target, arms, .. } => {
2865 let target_str = decompile_expr(target, interner);
2866 out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
2867 for arm in arms {
2868 if let Some(variant) = arm.variant {
2869 let variant_name = interner.resolve(variant);
2870 let bindings: Vec<String> = arm.bindings.iter()
2871 .map(|(_, b)| interner.resolve(*b).to_string())
2872 .collect();
2873 if bindings.is_empty() {
2874 out.push_str(&format!("{} When {}:\n", pad, variant_name));
2875 } else {
2876 out.push_str(&format!("{} When {}({}):\n", pad, variant_name, bindings.join(", ")));
2877 }
2878 } else {
2879 out.push_str(&format!("{} Otherwise:\n", pad));
2880 }
2881 for s in arm.body.iter() {
2882 decompile_stmt(s, interner, out, indent + 2);
2883 }
2884 }
2885 }
2886 Stmt::Pop { collection, into } => {
2887 let coll_str = decompile_expr(collection, interner);
2888 if let Some(target) = into {
2889 let target_name = interner.resolve(*target);
2890 out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
2891 } else {
2892 out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
2893 }
2894 }
2895 Stmt::Break => {
2896 out.push_str(&format!("{}Break.\n", pad));
2897 }
2898 Stmt::RuntimeAssert { condition } => {
2899 let cond_str = decompile_expr(condition, interner);
2900 out.push_str(&format!("{}Assert that {}.\n", pad, cond_str));
2901 }
2902 Stmt::Add { value, collection } => {
2903 let val_str = decompile_expr(value, interner);
2904 let coll_str = decompile_expr(collection, interner);
2905 out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
2906 }
2907 Stmt::Remove { value, collection } => {
2908 let val_str = decompile_expr(value, interner);
2909 let coll_str = decompile_expr(collection, interner);
2910 out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
2911 }
2912 Stmt::Zone { name, body, .. } => {
2913 let zone_name = interner.resolve(*name);
2914 out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
2915 for s in body.iter() {
2916 decompile_stmt(s, interner, out, indent + 1);
2917 }
2918 }
2919 Stmt::ReadFrom { var, .. } => {
2920 let var_name = interner.resolve(*var);
2921 out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
2922 }
2923 Stmt::WriteFile { content, path } => {
2924 let content_str = decompile_expr(content, interner);
2925 let path_str = decompile_expr(path, interner);
2926 out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
2927 }
2928 Stmt::Sleep { milliseconds } => {
2929 let ms = decompile_expr(milliseconds, interner);
2930 out.push_str(&format!("{}Sleep {}.\n", pad, ms));
2931 }
2932 _ => {
2933 }
2936 }
2937}
2938
2939fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
2940 match expr {
2941 Expr::Literal(lit) => match lit {
2942 Literal::Number(n) => n.to_string(),
2943 Literal::Float(f) => format!("{}", f),
2944 Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
2945 Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
2946 Literal::Nothing => "nothing".to_string(),
2947 Literal::Char(c) => format!("'{}'", c),
2948 Literal::Duration(ns) => format!("{}", ns),
2949 Literal::Date(days) => format!("{}", days),
2950 Literal::Moment(ns) => format!("{}", ns),
2951 Literal::Span { months, days } => format!("{} months {} days", months, days),
2952 Literal::Time(ns) => format!("{}", ns),
2953 },
2954 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2955 Expr::BinaryOp { op, left, right } => {
2956 let l = if matches!(left, Expr::BinaryOp { .. }) {
2957 format!("({})", decompile_expr(left, interner))
2958 } else {
2959 decompile_expr(left, interner)
2960 };
2961 let r = if matches!(right, Expr::BinaryOp { .. }) {
2962 format!("({})", decompile_expr(right, interner))
2963 } else {
2964 decompile_expr(right, interner)
2965 };
2966 if matches!(op, BinaryOpKind::Shl) {
2969 if let Expr::Literal(Literal::Number(k)) = right {
2971 let multiplier = 1i64 << k;
2972 return format!("{} * {}", l, multiplier);
2973 }
2974 }
2975 if matches!(op, BinaryOpKind::Shr) {
2976 if let Expr::Literal(Literal::Number(k)) = right {
2978 let divisor = 1i64 << k;
2979 return format!("{} / {}", l, divisor);
2980 }
2981 }
2982 let op_str = match op {
2983 BinaryOpKind::Add => "+",
2984 BinaryOpKind::Subtract => "-",
2985 BinaryOpKind::Multiply => "*",
2986 BinaryOpKind::Divide => "/",
2987 BinaryOpKind::Modulo => "%",
2988 BinaryOpKind::Eq => "equals",
2989 BinaryOpKind::NotEq => "is not",
2990 BinaryOpKind::Lt => "is less than",
2991 BinaryOpKind::Gt => "is greater than",
2992 BinaryOpKind::LtEq => "is at most",
2993 BinaryOpKind::GtEq => "is at least",
2994 BinaryOpKind::And => "and",
2995 BinaryOpKind::Or => "or",
2996 BinaryOpKind::Concat => "+",
2997 BinaryOpKind::BitXor => "+",
2998 BinaryOpKind::Shl => "*",
2999 BinaryOpKind::Shr => "/",
3000 };
3001 format!("{} {} {}", l, op_str, r)
3002 }
3003 Expr::Not { operand } => {
3004 let inner = decompile_expr(operand, interner);
3005 format!("not {}", inner)
3006 }
3007 Expr::Call { function, args } => {
3008 let fn_name = interner.resolve(*function);
3009 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3010 if arg_strs.is_empty() {
3011 format!("{}()", fn_name)
3012 } else {
3013 format!("{}({})", fn_name, arg_strs.join(", "))
3014 }
3015 }
3016 Expr::Index { collection, index } => {
3017 let coll = decompile_expr(collection, interner);
3018 let idx = decompile_expr(index, interner);
3019 format!("item {} of {}", idx, coll)
3020 }
3021 Expr::Length { collection } => {
3022 let coll = decompile_expr(collection, interner);
3023 format!("length of {}", coll)
3024 }
3025 Expr::FieldAccess { object, field } => {
3026 let obj = decompile_expr(object, interner);
3027 let field_name = interner.resolve(*field);
3028 format!("{} of {}", field_name, obj)
3029 }
3030 Expr::New { type_name, .. } => {
3031 let tn = interner.resolve(*type_name);
3032 format!("a new {}", tn)
3033 }
3034 Expr::NewVariant { variant, fields, .. } => {
3035 let vn = interner.resolve(*variant);
3036 if fields.is_empty() {
3037 format!("a new {}", vn)
3038 } else {
3039 let parts: Vec<String> = fields.iter().map(|(name, val)| {
3040 let n = interner.resolve(*name);
3041 let v = decompile_expr(val, interner);
3042 format!("{} {}", n, v)
3043 }).collect();
3044 format!("a new {} with {}", vn, parts.join(" and "))
3045 }
3046 }
3047 Expr::InterpolatedString(parts) => {
3048 let mut result = String::new();
3049 for part in parts {
3050 match part {
3051 StringPart::Literal(sym) => {
3052 result.push_str(&interner.resolve(*sym));
3053 }
3054 StringPart::Expr { value, debug, .. } => {
3055 let expr_str = decompile_expr(value, interner);
3056 if *debug {
3057 result.push_str(&format!("{{{}=}}", expr_str));
3058 } else {
3059 result.push_str(&format!("{{{}}}", expr_str));
3060 }
3061 }
3062 }
3063 }
3064 format!("\"{}\"", result)
3065 }
3066 Expr::Slice { collection, start, end } => {
3067 let coll = decompile_expr(collection, interner);
3068 let s = decompile_expr(start, interner);
3069 let e = decompile_expr(end, interner);
3070 format!("{} {} through {}", coll, s, e)
3071 }
3072 Expr::Copy { expr } => {
3073 let inner = decompile_expr(expr, interner);
3074 format!("copy of {}", inner)
3075 }
3076 Expr::Give { value } => {
3077 let inner = decompile_expr(value, interner);
3078 format!("Give {}", inner)
3079 }
3080 Expr::Contains { collection, value } => {
3081 let coll = decompile_expr(collection, interner);
3082 let val = decompile_expr(value, interner);
3083 format!("{} contains {}", coll, val)
3084 }
3085 Expr::Union { left, right } => {
3086 let l = decompile_expr(left, interner);
3087 let r = decompile_expr(right, interner);
3088 format!("{} union {}", l, r)
3089 }
3090 Expr::Intersection { left, right } => {
3091 let l = decompile_expr(left, interner);
3092 let r = decompile_expr(right, interner);
3093 format!("{} intersection {}", l, r)
3094 }
3095 Expr::List(elems) => {
3096 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3097 format!("[{}]", parts.join(", "))
3098 }
3099 Expr::Tuple(elems) => {
3100 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3101 format!("({})", parts.join(", "))
3102 }
3103 Expr::Range { start, end } => {
3104 let s = decompile_expr(start, interner);
3105 let e = decompile_expr(end, interner);
3106 format!("{} to {}", s, e)
3107 }
3108 Expr::OptionSome { value } => {
3109 let inner = decompile_expr(value, interner);
3110 format!("some {}", inner)
3111 }
3112 Expr::OptionNone => "none".to_string(),
3113 Expr::WithCapacity { value, capacity } => {
3114 let val = decompile_expr(value, interner);
3115 let cap = decompile_expr(capacity, interner);
3116 format!("{} with capacity {}", val, cap)
3117 }
3118 Expr::Escape { language, code } => {
3119 let lang = interner.resolve(*language);
3120 let src = interner.resolve(*code);
3121 format!("Escape to {}:\n{}", lang, src)
3122 }
3123 Expr::ManifestOf { zone } => {
3124 let z = decompile_expr(zone, interner);
3125 format!("the manifest of {}", z)
3126 }
3127 Expr::ChunkAt { index, zone } => {
3128 let idx = decompile_expr(index, interner);
3129 let z = decompile_expr(zone, interner);
3130 format!("the chunk at {} in {}", idx, z)
3131 }
3132 Expr::Closure { params, body, return_type } => {
3133 let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
3134 let n = interner.resolve(*name);
3135 let t = decompile_type_expr(ty, interner);
3136 format!("{}: {}", n, t)
3137 }).collect();
3138 let ret = if let Some(rt) = return_type {
3139 format!(" -> {}", decompile_type_expr(rt, interner))
3140 } else {
3141 String::new()
3142 };
3143 match body {
3144 ClosureBody::Expression(expr) => {
3145 let e = decompile_expr(expr, interner);
3146 format!("({}){} -> {}", param_strs.join(", "), ret, e)
3147 }
3148 ClosureBody::Block(_) => {
3149 format!("({}){} -> [block]", param_strs.join(", "), ret)
3150 }
3151 }
3152 }
3153 Expr::CallExpr { callee, args } => {
3154 let c = decompile_expr(callee, interner);
3155 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3156 format!("{}({})", c, arg_strs.join(", "))
3157 }
3158 }
3159}
3160
3161fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
3162 match ty {
3163 TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
3164 TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
3165 TypeExpr::Generic { base, params } => {
3166 let base_str = interner.resolve(*base);
3167 let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
3168 format!("{} of {}", base_str, param_strs.join(" and "))
3169 }
3170 TypeExpr::Function { inputs, output } => {
3171 let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
3172 let out_str = decompile_type_expr(output, interner);
3173 format!("fn({}) -> {}", in_strs.join(", "), out_str)
3174 }
3175 TypeExpr::Refinement { base, .. } => {
3176 decompile_type_expr(base, interner)
3177 }
3178 TypeExpr::Persistent { inner } => {
3179 format!("Persistent {}", decompile_type_expr(inner, interner))
3180 }
3181 }
3182}
3183
3184pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
3191 let mut interner = Interner::new();
3192 let mut lexer = Lexer::new(source, &mut interner);
3193 let tokens = lexer.tokenize();
3194
3195 let type_registry = {
3196 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3197 let result = discovery.run_full();
3198 result.types
3199 };
3200
3201 let mut world_state = WorldState::new();
3202 let expr_arena = Arena::new();
3203 let term_arena = Arena::new();
3204 let np_arena = Arena::new();
3205 let sym_arena = Arena::new();
3206 let role_arena = Arena::new();
3207 let pp_arena = Arena::new();
3208 let stmt_arena: Arena<Stmt> = Arena::new();
3209 let imperative_expr_arena: Arena<Expr> = Arena::new();
3210 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3211
3212 let ast_ctx = AstContext::with_types(
3213 &expr_arena, &term_arena, &np_arena, &sym_arena,
3214 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3215 &type_expr_arena,
3216 );
3217
3218 let mut parser = crate::parser::Parser::new(
3219 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3220 );
3221 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3222
3223 verify_no_overhead_stmts(&stmts, &interner)
3224}
3225
3226const CORE_VARIANT_NAMES: &[&str] = &[
3227 "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
3228 "CCall", "CIndex", "CLen", "CMapGet",
3229 "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
3230 "CCallS", "CPush", "CSetIdx", "CMapSet",
3231 "CFuncDef", "CProg",
3232 "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
3233];
3234
3235fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
3236 for stmt in stmts {
3237 check_stmt_overhead(stmt, interner)?;
3238 }
3239 Ok(())
3240}
3241
3242fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
3243 match stmt {
3244 Stmt::Inspect { arms, .. } => {
3245 for arm in arms {
3246 if let Some(variant) = arm.variant {
3247 let variant_name = interner.resolve(variant);
3248 if CORE_VARIANT_NAMES.contains(&variant_name) {
3249 return Err(format!(
3250 "Interpretive overhead: Inspect dispatches on Core variant '{}'",
3251 variant_name
3252 ));
3253 }
3254 }
3255 for s in arm.body.iter() {
3256 check_stmt_overhead(s, interner)?;
3257 }
3258 }
3259 }
3260 Stmt::If { cond, then_block, else_block } => {
3261 check_expr_overhead(cond, interner)?;
3262 for s in then_block.iter() {
3263 check_stmt_overhead(s, interner)?;
3264 }
3265 if let Some(els) = else_block {
3266 for s in els.iter() {
3267 check_stmt_overhead(s, interner)?;
3268 }
3269 }
3270 }
3271 Stmt::While { cond, body, .. } => {
3272 check_expr_overhead(cond, interner)?;
3273 for s in body.iter() {
3274 check_stmt_overhead(s, interner)?;
3275 }
3276 }
3277 Stmt::FunctionDef { body, .. } => {
3278 for s in body.iter() {
3279 check_stmt_overhead(s, interner)?;
3280 }
3281 }
3282 Stmt::Repeat { body, .. } => {
3283 for s in body.iter() {
3284 check_stmt_overhead(s, interner)?;
3285 }
3286 }
3287 Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
3288 check_expr_overhead(value, interner)?;
3289 }
3290 Stmt::Return { value } => {
3291 if let Some(v) = value {
3292 check_expr_overhead(v, interner)?;
3293 }
3294 }
3295 _ => {}
3296 }
3297 Ok(())
3298}
3299
3300fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
3301 match expr {
3302 Expr::Index { collection, index } => {
3303 if let Expr::Identifier(coll_sym) = collection {
3305 let coll_name = interner.resolve(*coll_sym);
3306 if coll_name == "env" {
3307 if let Expr::Literal(Literal::Text(_)) = index {
3308 return Err(
3309 "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
3310 );
3311 }
3312 }
3313 }
3314 check_expr_overhead(collection, interner)?;
3315 check_expr_overhead(index, interner)?;
3316 }
3317 Expr::New { type_name, .. } => {
3318 let tn = interner.resolve(*type_name);
3319 if CORE_VARIANT_NAMES.contains(&tn) {
3320 return Err(format!(
3321 "Interpretive overhead: Core type constructor 'new {}'", tn
3322 ));
3323 }
3324 }
3325 Expr::NewVariant { variant, .. } => {
3326 let vn = interner.resolve(*variant);
3327 if CORE_VARIANT_NAMES.contains(&vn) {
3328 return Err(format!(
3329 "Interpretive overhead: Core variant constructor '{}'", vn
3330 ));
3331 }
3332 }
3333 Expr::Call { function, args } => {
3334 let fn_name = interner.resolve(*function);
3335 if CORE_VARIANT_NAMES.contains(&fn_name) {
3336 return Err(format!(
3337 "Interpretive overhead: Core variant call '{}'", fn_name
3338 ));
3339 }
3340 for a in args {
3341 check_expr_overhead(a, interner)?;
3342 }
3343 }
3344 Expr::BinaryOp { left, right, .. } => {
3345 check_expr_overhead(left, interner)?;
3346 check_expr_overhead(right, interner)?;
3347 }
3348 Expr::Not { operand } => {
3349 check_expr_overhead(operand, interner)?;
3350 }
3351 Expr::Length { collection } => {
3352 check_expr_overhead(collection, interner)?;
3353 }
3354 Expr::FieldAccess { object, .. } => {
3355 check_expr_overhead(object, interner)?;
3356 }
3357 _ => {}
3358 }
3359 Ok(())
3360}
3361
3362pub fn pe_source_text() -> &'static str {
3368 include_str!("optimize/pe_source.logos")
3369}
3370
3371const CORE_TYPES_FOR_PE: &str = r#"
3372## A CExpr is one of:
3373 A CInt with value Int.
3374 A CFloat with value Real.
3375 A CBool with value Bool.
3376 A CText with value Text.
3377 A CVar with name Text.
3378 A CBinOp with op Text and left CExpr and right CExpr.
3379 A CNot with inner CExpr.
3380 A CCall with name Text and args Seq of CExpr.
3381 A CIndex with coll CExpr and idx CExpr.
3382 A CLen with target CExpr.
3383 A CMapGet with target CExpr and key CExpr.
3384 A CNewSeq.
3385 A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
3386 A CList with items Seq of CExpr.
3387 A CRange with start CExpr and end CExpr.
3388 A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
3389 A CCopy with target CExpr.
3390 A CNewSet.
3391 A CContains with coll CExpr and elem CExpr.
3392 A CUnion with left CExpr and right CExpr.
3393 A CIntersection with left CExpr and right CExpr.
3394 A COptionSome with inner CExpr.
3395 A COptionNone.
3396 A CTuple with items Seq of CExpr.
3397 A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
3398 A CFieldAccess with target CExpr and field Text.
3399 A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
3400 A CCallExpr with target CExpr and args Seq of CExpr.
3401 A CInterpolatedString with parts Seq of CStringPart.
3402 A CDuration with amount CExpr and unit Text.
3403 A CTimeNow.
3404 A CDateToday.
3405 A CEscExpr with code Text.
3406
3407## A CStringPart is one of:
3408 A CLiteralPart with value Text.
3409 A CExprPart with expr CExpr.
3410
3411## A CStmt is one of:
3412 A CLet with name Text and expr CExpr.
3413 A CSet with name Text and expr CExpr.
3414 A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
3415 A CWhile with cond CExpr and body Seq of CStmt.
3416 A CReturn with expr CExpr.
3417 A CShow with expr CExpr.
3418 A CCallS with name Text and args Seq of CExpr.
3419 A CPush with expr CExpr and target Text.
3420 A CSetIdx with target Text and idx CExpr and val CExpr.
3421 A CMapSet with target Text and key CExpr and val CExpr.
3422 A CPop with target Text.
3423 A CRepeat with var Text and coll CExpr and body Seq of CStmt.
3424 A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
3425 A CBreak.
3426 A CAdd with elem CExpr and target Text.
3427 A CRemove with elem CExpr and target Text.
3428 A CSetField with target Text and field Text and val CExpr.
3429 A CStructDef with name Text and fieldNames Seq of Text.
3430 A CInspect with target CExpr and arms Seq of CMatchArm.
3431 A CEnumDef with name Text and variants Seq of Text.
3432 A CRuntimeAssert with cond CExpr and msg CExpr.
3433 A CGive with expr CExpr and target Text.
3434 A CEscStmt with code Text.
3435 A CSleep with duration CExpr.
3436 A CReadConsole with target Text.
3437 A CReadFile with path CExpr and target Text.
3438 A CWriteFile with path CExpr and content CExpr.
3439 A CCheck with predicate CExpr and msg CExpr.
3440 A CAssert with proposition CExpr.
3441 A CTrust with proposition CExpr and justification Text.
3442 A CRequire with dependency Text.
3443 A CMerge with target Text and other CExpr.
3444 A CIncrease with target Text and amount CExpr.
3445 A CDecrease with target Text and amount CExpr.
3446 A CAppendToSeq with target Text and value CExpr.
3447 A CResolve with target Text.
3448 A CSync with target Text and channel CExpr.
3449 A CMount with target Text and path CExpr.
3450 A CConcurrent with branches Seq of Seq of CStmt.
3451 A CParallel with branches Seq of Seq of CStmt.
3452 A CLaunchTask with body Seq of CStmt and handle Text.
3453 A CStopTask with handle CExpr.
3454 A CSelect with branches Seq of CSelectBranch.
3455 A CCreatePipe with name Text and capacity CExpr.
3456 A CSendPipe with chan Text and value CExpr.
3457 A CReceivePipe with chan Text and target Text.
3458 A CTrySendPipe with chan Text and value CExpr.
3459 A CTryReceivePipe with chan Text and target Text.
3460 A CSpawn with agentType Text and target Text.
3461 A CSendMessage with target CExpr and msg CExpr.
3462 A CAwaitMessage with target Text.
3463 A CListen with addr CExpr and handler Text.
3464 A CConnectTo with addr CExpr and target Text.
3465 A CZone with name Text and kind Text and body Seq of CStmt.
3466
3467## A CSelectBranch is one of:
3468 A CSelectRecv with chan Text and var Text and body Seq of CStmt.
3469 A CSelectTimeout with duration CExpr and body Seq of CStmt.
3470
3471## A CMatchArm is one of:
3472 A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
3473 A COtherwise with body Seq of CStmt.
3474
3475## A CFunc is one of:
3476 A CFuncDef with name Text and params Seq of Text and body Seq of CStmt.
3477
3478## A CProgram is one of:
3479 A CProg with funcs Seq of CFunc and main Seq of CStmt.
3480
3481## A CVal is one of:
3482 A VInt with value Int.
3483 A VFloat with value Real.
3484 A VBool with value Bool.
3485 A VText with value Text.
3486 A VSeq with items Seq of CVal.
3487 A VMap with entries Map of Text to CVal.
3488 A VError with msg Text.
3489 A VNothing.
3490 A VSet with items Seq of CVal.
3491 A VOption with inner CVal and present Bool.
3492 A VTuple with items Seq of CVal.
3493 A VStruct with typeName Text and fields Map of Text to CVal.
3494 A VVariant with typeName Text and variantName Text and fields Seq of CVal.
3495 A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
3496 A VDuration with millis Int.
3497 A VDate with year Int and month Int and day Int.
3498 A VMoment with millis Int.
3499 A VSpan with startMillis Int and endMillis Int.
3500 A VTime with hour Int and minute Int and second Int.
3501 A VCrdt with kind Text and state Map of Text to CVal.
3502"#;
3503
3504pub fn quote_pe_source() -> Result<String, String> {
3512 let pe_source = pe_source_text();
3513 let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
3514 let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
3515 Ok(format!("{}\n{}", pe_source, encoded))
3516}
3517
3518pub fn projection2_source() -> Result<String, String> {
3532 let pe_source = pe_source_text();
3533
3534 let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
3535
3536 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
3537}
3538
3539pub fn projection3_source() -> Result<String, String> {
3554 let pe_source = pe_source_text();
3555
3556 let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
3557
3558 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
3559}
3560
3561fn replace_word(source: &str, from: &str, to: &str) -> String {
3562 let mut result = String::with_capacity(source.len());
3563 let mut remaining = source;
3564 while let Some(pos) = remaining.find(from) {
3565 let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
3566 let after_pos = pos + from.len();
3567 let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
3568 let is_word = !before.is_ascii_alphanumeric() && before != b'_'
3569 && !after.is_ascii_alphanumeric() && after != b'_';
3570 result.push_str(&remaining[..pos]);
3571 if is_word {
3572 result.push_str(to);
3573 } else {
3574 result.push_str(from);
3575 }
3576 remaining = &remaining[after_pos..];
3577 }
3578 result.push_str(remaining);
3579 result
3580}
3581
3582#[cfg(test)]
3583mod tests {
3584 use super::*;
3585
3586 #[test]
3587 fn test_compile_let_statement() {
3588 let source = "## Main\nLet x be 5.";
3589 let result = compile_to_rust(source);
3590 assert!(result.is_ok(), "Should compile: {:?}", result);
3591 let rust = result.unwrap();
3592 assert!(rust.contains("fn main()"));
3593 assert!(rust.contains("let x = 5;"));
3594 }
3595
3596 #[test]
3597 fn test_compile_return_statement() {
3598 let source = "## Main\nReturn 42.";
3599 let result = compile_to_rust(source);
3600 assert!(result.is_ok(), "Should compile: {:?}", result);
3601 let rust = result.unwrap();
3602 assert!(rust.contains("return 42;"));
3603 }
3604
3605}