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<String>, 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, return_type, is_native, .. } = stmt {
1307 if *is_native {
1308 continue; }
1310 let fn_name = interner.resolve(*name).to_string();
1311 let param_names: Vec<String> = params
1312 .iter()
1313 .map(|(name, _)| interner.resolve(*name).to_string())
1314 .collect();
1315 let param_types: Vec<String> = params
1316 .iter()
1317 .map(|(_, ty)| decompile_type_expr(ty, &interner))
1318 .collect();
1319 let ret_type = return_type
1320 .map(|rt| decompile_type_expr(rt, &interner))
1321 .unwrap_or_else(|| "Nothing".to_string());
1322 let body_stmts: Vec<&Stmt> = body.iter().collect();
1323 functions.push((fn_name, param_names, param_types, ret_type, body_stmts));
1324 } else {
1325 main_stmts.push(stmt);
1326 }
1327 }
1328
1329 let mut counter = 0usize;
1330 let mut output = String::new();
1331
1332 output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1334
1335 for (fn_name, params, param_types, ret_type, body) in &functions {
1336 let body_var = encode_stmt_list_src(body, &mut counter, &mut output, &interner, &variant_constructors);
1337
1338 let params_var = format!("params_{}", counter);
1339 counter += 1;
1340 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1341 for p in params {
1342 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1343 }
1344
1345 let param_types_var = format!("paramTypes_{}", counter);
1346 counter += 1;
1347 output.push_str(&format!("Let {} be a new Seq of Text.\n", param_types_var));
1348 for pt in param_types {
1349 output.push_str(&format!("Push \"{}\" to {}.\n", pt, param_types_var));
1350 }
1351
1352 let func_var = format!("func_{}", counter);
1353 counter += 1;
1354 output.push_str(&format!(
1355 "Let {} be a new CFuncDef with name \"{}\" and params {} and paramTypes {} and returnType \"{}\" and body {}.\n",
1356 func_var, fn_name, params_var, param_types_var, ret_type, body_var
1357 ));
1358 output.push_str(&format!(
1359 "Set item \"{}\" of encodedFuncMap to {}.\n",
1360 fn_name, func_var
1361 ));
1362 }
1363
1364 let main_var = encode_stmt_list_src(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1366 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1367
1368 Ok(output)
1369}
1370
1371pub fn encode_program_source_compact(source: &str) -> Result<String, ParseError> {
1375 let full_source = if source.contains("## Main") || source.contains("## To ") {
1376 source.to_string()
1377 } else {
1378 format!("## Main\n{}", source)
1379 };
1380
1381 let mut interner = Interner::new();
1382 let mut lexer = Lexer::new(&full_source, &mut interner);
1383 let tokens = lexer.tokenize();
1384
1385 let type_registry = {
1386 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1387 let result = discovery.run_full();
1388 result.types
1389 };
1390
1391 let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
1392 for (_type_name, type_def) in type_registry.iter_types() {
1393 if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
1394 for variant in variants {
1395 let vname = interner.resolve(variant.name).to_string();
1396 let field_names: Vec<String> = variant.fields.iter()
1397 .map(|f| interner.resolve(f.name).to_string())
1398 .collect();
1399 variant_constructors.insert(vname, field_names);
1400 }
1401 }
1402 }
1403
1404 let mut world_state = WorldState::new();
1405 let expr_arena = Arena::new();
1406 let term_arena = Arena::new();
1407 let np_arena = Arena::new();
1408 let sym_arena = Arena::new();
1409 let role_arena = Arena::new();
1410 let pp_arena = Arena::new();
1411 let stmt_arena: Arena<Stmt> = Arena::new();
1412 let imperative_expr_arena: Arena<Expr> = Arena::new();
1413 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1414
1415 let ast_ctx = AstContext::with_types(
1416 &expr_arena, &term_arena, &np_arena, &sym_arena,
1417 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
1418 &type_expr_arena,
1419 );
1420
1421 let mut parser = crate::parser::Parser::new(
1422 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
1423 );
1424 let stmts = parser.parse_program()?;
1425
1426 let mut functions: Vec<(String, Vec<String>, Vec<String>, String, Vec<&Stmt>)> = Vec::new();
1427 let mut main_stmts: Vec<&Stmt> = Vec::new();
1428
1429 for stmt in &stmts {
1430 if let Stmt::FunctionDef { name, params, body, return_type, is_native, .. } = stmt {
1431 if *is_native { continue; }
1432 let fn_name = interner.resolve(*name).to_string();
1433 let param_names: Vec<String> = params
1434 .iter()
1435 .map(|(name, _)| interner.resolve(*name).to_string())
1436 .collect();
1437 let param_types: Vec<String> = params
1438 .iter()
1439 .map(|(_, ty)| decompile_type_expr(ty, &interner))
1440 .collect();
1441 let ret_type = return_type
1442 .map(|rt| decompile_type_expr(rt, &interner))
1443 .unwrap_or_else(|| "Nothing".to_string());
1444 let body_stmts: Vec<&Stmt> = body.iter().collect();
1445 functions.push((fn_name, param_names, param_types, ret_type, body_stmts));
1446 } else {
1447 main_stmts.push(stmt);
1448 }
1449 }
1450
1451 let mut counter = 0usize;
1452 let mut output = String::new();
1453
1454 output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1455
1456 for (fn_name, params, param_types, ret_type, body) in &functions {
1457 let body_var = encode_stmt_list_compact(body, &mut counter, &mut output, &interner, &variant_constructors);
1458
1459 let params_var = format!("params_{}", counter);
1460 counter += 1;
1461 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1462 for p in params {
1463 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1464 }
1465
1466 let param_types_var = format!("paramTypes_{}", counter);
1467 counter += 1;
1468 output.push_str(&format!("Let {} be a new Seq of Text.\n", param_types_var));
1469 for pt in param_types {
1470 output.push_str(&format!("Push \"{}\" to {}.\n", pt, param_types_var));
1471 }
1472
1473 let func_var = format!("func_{}", counter);
1474 counter += 1;
1475 output.push_str(&format!(
1476 "Let {} be a new CFuncDef with name \"{}\" and params {} and paramTypes {} and returnType \"{}\" and body {}.\n",
1477 func_var, fn_name, params_var, param_types_var, ret_type, body_var
1478 ));
1479 output.push_str(&format!(
1480 "Set item \"{}\" of encodedFuncMap to {}.\n",
1481 fn_name, func_var
1482 ));
1483 }
1484
1485 let main_var = encode_stmt_list_compact(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1486 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1487
1488 Ok(output)
1489}
1490
1491fn try_inline_expr(expr: &Expr, interner: &Interner) -> Option<String> {
1494 match expr {
1495 Expr::Literal(lit) => match lit {
1496 Literal::Number(n) => Some(format!("(a new CInt with value {})", n)),
1497 Literal::Boolean(b) => Some(format!("(a new CBool with value {})", b)),
1498 Literal::Text(s) => {
1499 let text = interner.resolve(*s);
1500 Some(format!("(a new CText with value \"{}\")", text))
1501 }
1502 Literal::Float(f) => {
1503 let fs = format!("{}", f);
1504 let fs = if fs.contains('.') { fs } else { format!("{}.0", fs) };
1505 Some(format!("(a new CFloat with value {})", fs))
1506 }
1507 Literal::Nothing => Some("(a new CText with value \"nothing\")".to_string()),
1508 _ => None,
1509 },
1510 Expr::Identifier(sym) => {
1511 let name = interner.resolve(*sym);
1512 Some(format!("(a new CVar with name \"{}\")", name))
1513 }
1514 Expr::Not { operand } => {
1515 if let Some(inner) = try_inline_expr(operand, interner) {
1516 Some(format!("(a new CNot with inner {})", inner))
1517 } else {
1518 None
1519 }
1520 }
1521 Expr::OptionNone => Some("(a new COptionNone)".to_string()),
1522 _ => None,
1523 }
1524}
1525
1526fn encode_expr_compact(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1527 if let Some(inline) = try_inline_expr(expr, interner) {
1529 return inline;
1530 }
1531
1532 let var = format!("e_{}", *counter);
1534 *counter += 1;
1535
1536 match expr {
1537 Expr::BinaryOp { op, left, right } => {
1538 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1539 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1540 let op_str = match op {
1541 BinaryOpKind::Add => "+",
1542 BinaryOpKind::Subtract => "-",
1543 BinaryOpKind::Multiply => "*",
1544 BinaryOpKind::Divide => "/",
1545 BinaryOpKind::Modulo => "%",
1546 BinaryOpKind::Eq => "==",
1547 BinaryOpKind::NotEq => "!=",
1548 BinaryOpKind::Lt => "<",
1549 BinaryOpKind::Gt => ">",
1550 BinaryOpKind::LtEq => "<=",
1551 BinaryOpKind::GtEq => ">=",
1552 BinaryOpKind::And => "&&",
1553 BinaryOpKind::Or => "||",
1554 BinaryOpKind::Concat => "+",
1555 BinaryOpKind::BitXor => "^",
1556 BinaryOpKind::Shl => "<<",
1557 BinaryOpKind::Shr => ">>",
1558 };
1559 output.push_str(&format!(
1560 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
1561 var, op_str, left_var, right_var
1562 ));
1563 }
1564 Expr::Call { function, args } => {
1565 let fn_name = interner.resolve(*function);
1566 if let Some(field_names) = variants.get(fn_name) {
1567 let names_var = format!("nvNames_{}", *counter);
1568 *counter += 1;
1569 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1570 let vals_var = format!("nvVals_{}", *counter);
1571 *counter += 1;
1572 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1573 for (i, arg) in args.iter().enumerate() {
1574 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
1575 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1576 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1577 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
1578 }
1579 output.push_str(&format!(
1580 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1581 var, fn_name, names_var, vals_var
1582 ));
1583 } else {
1584 let args_var = format!("callArgs_{}", *counter);
1585 *counter += 1;
1586 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1587 for arg in args {
1588 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1589 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1590 }
1591 output.push_str(&format!(
1592 "Let {} be a new CCall with name \"{}\" and args {}.\n",
1593 var, fn_name, args_var
1594 ));
1595 }
1596 }
1597 Expr::Index { collection, index } => {
1598 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1599 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1600 output.push_str(&format!(
1601 "Let {} be a new CIndex with coll {} and idx {}.\n",
1602 var, coll_var, idx_var
1603 ));
1604 }
1605 Expr::Length { collection } => {
1606 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1607 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
1608 }
1609 Expr::FieldAccess { object, field } => {
1610 let obj_var = encode_expr_compact(object, counter, output, interner, variants);
1611 let field_name = interner.resolve(*field);
1612 output.push_str(&format!(
1613 "Let {} be a new CMapGet with target {} and key (a new CText with value \"{}\").\n",
1614 var, obj_var, field_name
1615 ));
1616 }
1617 Expr::NewVariant { variant, fields, .. } => {
1618 let variant_name = interner.resolve(*variant);
1619 let names_var = format!("nvNames_{}", *counter);
1620 *counter += 1;
1621 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1622 let vals_var = format!("nvVals_{}", *counter);
1623 *counter += 1;
1624 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1625 for (field_name, field_expr) in fields {
1626 let fname = interner.resolve(*field_name);
1627 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1628 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1629 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1630 }
1631 output.push_str(&format!(
1632 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1633 var, variant_name, names_var, vals_var
1634 ));
1635 }
1636 Expr::New { type_name, init_fields, .. } => {
1637 let tn = interner.resolve(*type_name);
1638 if tn == "Seq" || tn == "List" {
1639 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
1640 } else if tn == "Set" {
1641 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
1642 } else {
1643 let names_var = format!("nvNames_{}", *counter);
1644 *counter += 1;
1645 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1646 let vals_var = format!("nvVals_{}", *counter);
1647 *counter += 1;
1648 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1649 for (field_name, field_expr) in init_fields {
1650 let fname = interner.resolve(*field_name);
1651 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1652 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1653 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1654 }
1655 output.push_str(&format!(
1656 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1657 var, tn, names_var, vals_var
1658 ));
1659 }
1660 }
1661 Expr::InterpolatedString(parts) => {
1662 if parts.is_empty() {
1663 output.push_str(&format!("Let {} be (a new CText with value \"\").\n", var));
1664 } else {
1665 let mut part_vars: Vec<String> = Vec::new();
1666 for part in parts {
1667 match part {
1668 StringPart::Literal(sym) => {
1669 let text = interner.resolve(*sym);
1670 part_vars.push(format!("(a new CText with value \"{}\")", text));
1671 }
1672 StringPart::Expr { value, .. } => {
1673 let pv = encode_expr_compact(value, counter, output, interner, variants);
1674 part_vars.push(pv);
1675 }
1676 }
1677 }
1678 if part_vars.len() == 1 {
1679 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
1680 } else {
1681 let mut acc = part_vars[0].clone();
1682 for pv in &part_vars[1..] {
1683 let concat_var = format!("e_{}", *counter);
1684 *counter += 1;
1685 output.push_str(&format!(
1686 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
1687 concat_var, acc, pv
1688 ));
1689 acc = concat_var;
1690 }
1691 output.push_str(&format!("Let {} be {}.\n", var, acc));
1692 }
1693 }
1694 }
1695 Expr::Range { start, end } => {
1696 let start_var = encode_expr_compact(start, counter, output, interner, variants);
1697 let end_var = encode_expr_compact(end, counter, output, interner, variants);
1698 output.push_str(&format!(
1699 "Let {} be a new CRange with start {} and end {}.\n",
1700 var, start_var, end_var
1701 ));
1702 }
1703 Expr::Copy { expr } => {
1704 let inner_var = encode_expr_compact(expr, counter, output, interner, variants);
1705 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
1706 }
1707 Expr::Contains { collection, value } => {
1708 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1709 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1710 output.push_str(&format!(
1711 "Let {} be a new CContains with coll {} and elem {}.\n",
1712 var, coll_var, val_var
1713 ));
1714 }
1715 Expr::OptionSome { value } => {
1716 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1717 output.push_str(&format!(
1718 "Let {} be a new COptionSome with inner {}.\n", var, inner_var
1719 ));
1720 }
1721 Expr::Tuple(elems) => {
1722 let items_var = format!("tupItems_{}", *counter);
1723 *counter += 1;
1724 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
1725 for elem in elems {
1726 let elem_var = encode_expr_compact(elem, counter, output, interner, variants);
1727 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
1728 }
1729 output.push_str(&format!(
1730 "Let {} be a new CTuple with items {}.\n", var, items_var
1731 ));
1732 }
1733 Expr::Closure { params, body, .. } => {
1734 let params_var = format!("clp_{}", *counter);
1735 *counter += 1;
1736 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1737 let mut param_names = HashSet::new();
1738 for (sym, _) in params {
1739 let name = interner.resolve(*sym);
1740 param_names.insert(name.to_string());
1741 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
1742 }
1743 let body_var = format!("clb_{}", *counter);
1744 *counter += 1;
1745 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
1746 match body {
1747 ClosureBody::Expression(e) => {
1748 let ret_expr = encode_expr_compact(e, counter, output, interner, variants);
1749 let ret_var = format!("s_{}", *counter);
1750 *counter += 1;
1751 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
1752 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
1753 }
1754 ClosureBody::Block(stmts) => {
1755 for s in stmts.iter() {
1756 let sv = encode_stmt_compact(s, counter, output, interner, variants);
1757 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
1758 }
1759 }
1760 }
1761 let bound: HashSet<String> = param_names;
1762 let free = collect_free_vars_expr(expr, interner, &bound);
1763 let cap_var = format!("clc_{}", *counter);
1764 *counter += 1;
1765 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
1766 for fv in &free {
1767 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
1768 }
1769 output.push_str(&format!(
1770 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
1771 var, params_var, body_var, cap_var
1772 ));
1773 }
1774 Expr::CallExpr { callee, args } => {
1775 let callee_var = encode_expr_compact(callee, counter, output, interner, variants);
1776 let args_var = format!("cea_{}", *counter);
1777 *counter += 1;
1778 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1779 for a in args {
1780 let av = encode_expr_compact(a, counter, output, interner, variants);
1781 output.push_str(&format!("Push {} to {}.\n", av, args_var));
1782 }
1783 output.push_str(&format!(
1784 "Let {} be a new CCallExpr with target {} and args {}.\n",
1785 var, callee_var, args_var
1786 ));
1787 }
1788 Expr::Slice { collection, start, end } => {
1789 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1790 let start_var = encode_expr_compact(start, counter, output, interner, variants);
1791 let end_var = encode_expr_compact(end, counter, output, interner, variants);
1792 output.push_str(&format!(
1793 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
1794 var, coll_var, start_var, end_var
1795 ));
1796 }
1797 Expr::Union { left, right } => {
1798 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1799 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1800 output.push_str(&format!(
1801 "Let {} be a new CUnion with left {} and right {}.\n",
1802 var, left_var, right_var
1803 ));
1804 }
1805 Expr::Intersection { left, right } => {
1806 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1807 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1808 output.push_str(&format!(
1809 "Let {} be a new CIntersection with left {} and right {}.\n",
1810 var, left_var, right_var
1811 ));
1812 }
1813 Expr::Give { value } => {
1814 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1815 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
1816 }
1817 Expr::Escape { code, .. } => {
1818 let code_str = interner.resolve(*code);
1819 output.push_str(&format!(
1820 "Let {} be a new CEscExpr with code \"{}\".\n",
1821 var, code_str.replace('\"', "\\\"")
1822 ));
1823 }
1824 _ => {
1825 output.push_str(&format!("Let {} be (a new CText with value \"unsupported\").\n", var));
1827 }
1828 }
1829
1830 var
1831}
1832
1833fn encode_stmt_compact(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1834 let var = format!("s_{}", *counter);
1835 *counter += 1;
1836
1837 match stmt {
1838 Stmt::Let { var: name, value, .. } => {
1839 let name_str = interner.resolve(*name);
1840 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1841 output.push_str(&format!(
1842 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
1843 var, name_str, expr_var
1844 ));
1845 }
1846 Stmt::Set { target, value } => {
1847 let name_str = interner.resolve(*target);
1848 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1849 output.push_str(&format!(
1850 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
1851 var, name_str, expr_var
1852 ));
1853 }
1854 Stmt::If { cond, then_block, else_block } => {
1855 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1856 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
1857 let then_var = encode_stmt_list_compact(&then_stmts, counter, output, interner, variants);
1858 let else_var = if let Some(els) = else_block {
1859 let else_stmts: Vec<&Stmt> = els.iter().collect();
1860 encode_stmt_list_compact(&else_stmts, counter, output, interner, variants)
1861 } else {
1862 let empty_var = format!("emptyBlock_{}", *counter);
1863 *counter += 1;
1864 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
1865 empty_var
1866 };
1867 output.push_str(&format!(
1868 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
1869 var, cond_var, then_var, else_var
1870 ));
1871 }
1872 Stmt::While { cond, body, .. } => {
1873 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1874 let body_stmts: Vec<&Stmt> = body.iter().collect();
1875 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1876 output.push_str(&format!(
1877 "Let {} be a new CWhile with cond {} and body {}.\n",
1878 var, cond_var, body_var
1879 ));
1880 }
1881 Stmt::Return { value } => {
1882 if let Some(val) = value {
1883 let expr_var = encode_expr_compact(val, counter, output, interner, variants);
1884 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
1885 } else {
1886 output.push_str(&format!("Let {} be a new CReturn with expr (a new CText with value \"nothing\").\n", var));
1887 }
1888 }
1889 Stmt::Show { object, .. } => {
1890 let expr_var = encode_expr_compact(object, counter, output, interner, variants);
1891 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
1892 }
1893 Stmt::Repeat { pattern, iterable, body } => {
1894 let var_str = match pattern {
1895 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
1896 Pattern::Tuple(syms) => {
1897 if let Some(s) = syms.first() {
1898 interner.resolve(*s).to_string()
1899 } else {
1900 "item".to_string()
1901 }
1902 }
1903 };
1904 let coll_var = encode_expr_compact(iterable, counter, output, interner, variants);
1905 let body_stmts: Vec<&Stmt> = body.iter().collect();
1906 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1907 output.push_str(&format!(
1908 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
1909 var, var_str, coll_var, body_var
1910 ));
1911 }
1912 Stmt::Push { value, collection } => {
1913 let coll_name = extract_ident_name(collection, interner);
1914 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1915 output.push_str(&format!(
1916 "Let {} be a new CPush with expr {} and target \"{}\".\n",
1917 var, expr_var, coll_name
1918 ));
1919 }
1920 Stmt::SetIndex { collection, index, value } => {
1921 let target_str = extract_ident_name(collection, interner);
1922 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1923 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1924 output.push_str(&format!(
1925 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
1926 var, target_str, idx_var, val_var
1927 ));
1928 }
1929 Stmt::SetField { object, field, value } => {
1930 let target_str = extract_ident_name(object, interner);
1931 let field_str = interner.resolve(*field);
1932 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1933 output.push_str(&format!(
1934 "Let {} be a new CSetField with target \"{}\" and field \"{}\" and val {}.\n",
1935 var, target_str, field_str, val_var
1936 ));
1937 }
1938 Stmt::Break => {
1939 output.push_str(&format!("Let {} be a new CBreak.\n", var));
1940 }
1941 Stmt::Inspect { target, arms, .. } => {
1942 let target_var = encode_expr_compact(target, counter, output, interner, variants);
1943 let arms_var = format!("arms_{}", *counter);
1944 *counter += 1;
1945 output.push_str(&format!("Let {} be a new Seq of CMatchArm.\n", arms_var));
1946 for arm in arms {
1947 if let Some(variant_sym) = arm.variant {
1948 let vname = interner.resolve(variant_sym);
1949 let bindings_var = format!("bindings_{}", *counter);
1950 *counter += 1;
1951 output.push_str(&format!("Let {} be a new Seq of Text.\n", bindings_var));
1952 for (_, binding_name) in &arm.bindings {
1953 let bn = interner.resolve(*binding_name);
1954 output.push_str(&format!("Push \"{}\" to {}.\n", bn, bindings_var));
1955 }
1956 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1957 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1958 let arm_var = format!("arm_{}", *counter);
1959 *counter += 1;
1960 output.push_str(&format!(
1961 "Let {} be a new CWhen with variantName \"{}\" and bindings {} and body {}.\n",
1962 arm_var, vname, bindings_var, body_var
1963 ));
1964 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1965 } else {
1966 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1967 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1968 let arm_var = format!("arm_{}", *counter);
1969 *counter += 1;
1970 output.push_str(&format!(
1971 "Let {} be a new COtherwise with body {}.\n",
1972 arm_var, body_var
1973 ));
1974 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1975 }
1976 }
1977 output.push_str(&format!(
1978 "Let {} be a new CInspect with target {} and arms {}.\n",
1979 var, target_var, arms_var
1980 ));
1981 }
1982 _ => {
1983 return encode_stmt_src(stmt, counter, output, interner, variants);
1985 }
1986 }
1987
1988 var
1989}
1990
1991fn encode_stmt_list_compact(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1992 let list_var = format!("stmts_{}", *counter);
1993 *counter += 1;
1994 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
1995 for s in stmts {
1996 let sv = encode_stmt_compact(s, counter, output, interner, variants);
1997 output.push_str(&format!("Push {} to {}.\n", sv, list_var));
1998 }
1999 list_var
2000}
2001
2002fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
2003 let mut free = HashSet::new();
2004 match expr {
2005 Expr::Identifier(sym) => {
2006 let name = interner.resolve(*sym).to_string();
2007 if !bound.contains(&name) {
2008 free.insert(name);
2009 }
2010 }
2011 Expr::BinaryOp { left, right, .. } => {
2012 free.extend(collect_free_vars_expr(left, interner, bound));
2013 free.extend(collect_free_vars_expr(right, interner, bound));
2014 }
2015 Expr::Not { operand } => {
2016 free.extend(collect_free_vars_expr(operand, interner, bound));
2017 }
2018 Expr::Copy { expr: inner } => {
2019 free.extend(collect_free_vars_expr(inner, interner, bound));
2020 }
2021 Expr::CallExpr { callee, args } => {
2022 free.extend(collect_free_vars_expr(callee, interner, bound));
2023 for a in args {
2024 free.extend(collect_free_vars_expr(a, interner, bound));
2025 }
2026 }
2027 Expr::Index { collection, index } => {
2028 free.extend(collect_free_vars_expr(collection, interner, bound));
2029 free.extend(collect_free_vars_expr(index, interner, bound));
2030 }
2031 Expr::InterpolatedString(parts) => {
2032 for part in parts {
2033 if let StringPart::Expr { value, .. } = part {
2034 free.extend(collect_free_vars_expr(value, interner, bound));
2035 }
2036 }
2037 }
2038 Expr::Closure { params, body, .. } => {
2039 let mut inner_bound = bound.clone();
2040 for (sym, _) in params {
2041 inner_bound.insert(interner.resolve(*sym).to_string());
2042 }
2043 match body {
2044 ClosureBody::Expression(e) => {
2045 free.extend(collect_free_vars_expr(e, interner, &inner_bound));
2046 }
2047 ClosureBody::Block(stmts) => {
2048 for s in stmts.iter() {
2049 free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
2050 }
2051 }
2052 }
2053 }
2054 _ => {}
2055 }
2056 free
2057}
2058
2059fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
2060 let mut free = HashSet::new();
2061 match stmt {
2062 Stmt::Let { var, value, .. } => {
2063 free.extend(collect_free_vars_expr(value, interner, bound));
2064 }
2065 Stmt::Set { target, value, .. } => {
2066 let n = interner.resolve(*target).to_string();
2067 if !bound.contains(&n) {
2068 free.insert(n);
2069 }
2070 free.extend(collect_free_vars_expr(value, interner, bound));
2071 }
2072 Stmt::Show { object, .. } => {
2073 free.extend(collect_free_vars_expr(object, interner, bound));
2074 }
2075 Stmt::Return { value } => {
2076 if let Some(v) = value {
2077 free.extend(collect_free_vars_expr(v, interner, bound));
2078 }
2079 }
2080 _ => {}
2081 }
2082 free
2083}
2084
2085fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2086 let var = format!("e_{}", *counter);
2087 *counter += 1;
2088
2089 match expr {
2090 Expr::Literal(lit) => match lit {
2091 Literal::Number(n) => {
2092 output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
2093 }
2094 Literal::Boolean(b) => {
2095 output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
2096 }
2097 Literal::Text(s) => {
2098 let text = interner.resolve(*s);
2099 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
2100 }
2101 Literal::Float(f) => {
2102 output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, f));
2103 }
2104 Literal::Duration(nanos) => {
2105 let millis = nanos / 1_000_000;
2106 let amount_var = format!("e_{}", *counter);
2107 *counter += 1;
2108 output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
2109 output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
2110 }
2111 Literal::Nothing => {
2112 output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
2113 }
2114 _ => {
2115 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2116 }
2117 },
2118 Expr::Identifier(sym) => {
2119 let name = interner.resolve(*sym);
2120 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
2121 }
2122 Expr::BinaryOp { op, left, right } => {
2123 let left_var = encode_expr_src(left, counter, output, interner, variants);
2124 let right_var = encode_expr_src(right, counter, output, interner, variants);
2125 let op_str = match op {
2126 BinaryOpKind::Add => "+",
2127 BinaryOpKind::Subtract => "-",
2128 BinaryOpKind::Multiply => "*",
2129 BinaryOpKind::Divide => "/",
2130 BinaryOpKind::Modulo => "%",
2131 BinaryOpKind::Eq => "==",
2132 BinaryOpKind::NotEq => "!=",
2133 BinaryOpKind::Lt => "<",
2134 BinaryOpKind::Gt => ">",
2135 BinaryOpKind::LtEq => "<=",
2136 BinaryOpKind::GtEq => ">=",
2137 BinaryOpKind::And => "&&",
2138 BinaryOpKind::Or => "||",
2139 BinaryOpKind::Concat => "+",
2140 BinaryOpKind::BitXor => "^",
2141 BinaryOpKind::Shl => "<<",
2142 BinaryOpKind::Shr => ">>",
2143 };
2144 output.push_str(&format!(
2145 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
2146 var, op_str, left_var, right_var
2147 ));
2148 }
2149 Expr::Not { operand } => {
2150 let inner_var = encode_expr_src(operand, counter, output, interner, variants);
2151 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
2152 }
2153 Expr::Call { function, args } => {
2154 let fn_name = interner.resolve(*function);
2155 if let Some(field_names) = variants.get(fn_name) {
2156 let names_var = format!("nvNames_{}", *counter);
2158 *counter += 1;
2159 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2160 let vals_var = format!("nvVals_{}", *counter);
2161 *counter += 1;
2162 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2163 for (i, arg) in args.iter().enumerate() {
2164 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
2165 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2166 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2167 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
2168 }
2169 output.push_str(&format!(
2170 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2171 var, fn_name, names_var, vals_var
2172 ));
2173 } else {
2174 let args_var = format!("callArgs_{}", *counter);
2175 *counter += 1;
2176 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2177 for arg in args {
2178 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2179 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2180 }
2181 output.push_str(&format!(
2182 "Let {} be a new CCall with name \"{}\" and args {}.\n",
2183 var, fn_name, args_var
2184 ));
2185 }
2186 }
2187 Expr::Index { collection, index } => {
2188 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2189 let idx_var = encode_expr_src(index, counter, output, interner, variants);
2190 output.push_str(&format!(
2191 "Let {} be a new CIndex with coll {} and idx {}.\n",
2192 var, coll_var, idx_var
2193 ));
2194 }
2195 Expr::Length { collection } => {
2196 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2197 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
2198 }
2199 Expr::FieldAccess { object, field } => {
2200 let obj_var = encode_expr_src(object, counter, output, interner, variants);
2201 let field_name = interner.resolve(*field);
2202 let key_var = format!("e_{}", *counter);
2203 *counter += 1;
2204 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2205 output.push_str(&format!(
2206 "Let {} be a new CMapGet with target {} and key {}.\n",
2207 var, obj_var, key_var
2208 ));
2209 }
2210 Expr::NewVariant { variant, fields, .. } => {
2211 let variant_name = interner.resolve(*variant);
2212 let names_var = format!("nvNames_{}", *counter);
2213 *counter += 1;
2214 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2215 let vals_var = format!("nvVals_{}", *counter);
2216 *counter += 1;
2217 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2218 for (field_name, field_expr) in fields {
2219 let fname = interner.resolve(*field_name);
2220 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2221 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2222 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2223 }
2224 output.push_str(&format!(
2225 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2226 var, variant_name, names_var, vals_var
2227 ));
2228 }
2229 Expr::New { type_name, init_fields, .. } => {
2230 let tn = interner.resolve(*type_name);
2231 if tn == "Seq" || tn == "List" {
2232 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
2233 } else if tn == "Set" {
2234 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
2235 } else if tn == "Map" || tn.starts_with("Map ") {
2236 let names_var = format!("nvNames_{}", *counter);
2238 *counter += 1;
2239 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2240 let vals_var = format!("nvVals_{}", *counter);
2241 *counter += 1;
2242 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2243 output.push_str(&format!(
2244 "Let {} be a new CNew with typeName \"Map\" and fieldNames {} and fields {}.\n",
2245 var, names_var, vals_var
2246 ));
2247 } else if init_fields.is_empty() {
2248 let names_var = format!("nvNames_{}", *counter);
2249 *counter += 1;
2250 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2251 let vals_var = format!("nvVals_{}", *counter);
2252 *counter += 1;
2253 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2254 output.push_str(&format!(
2255 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2256 var, tn, names_var, vals_var
2257 ));
2258 } else {
2259 let names_var = format!("nvNames_{}", *counter);
2260 *counter += 1;
2261 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2262 let vals_var = format!("nvVals_{}", *counter);
2263 *counter += 1;
2264 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2265 for (field_name, field_expr) in init_fields {
2266 let fname = interner.resolve(*field_name);
2267 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2268 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2269 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2270 }
2271 output.push_str(&format!(
2272 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2273 var, tn, names_var, vals_var
2274 ));
2275 }
2276 }
2277 Expr::InterpolatedString(parts) => {
2278 if parts.is_empty() {
2279 output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
2280 } else {
2281 let mut part_vars: Vec<String> = Vec::new();
2282 for part in parts {
2283 match part {
2284 StringPart::Literal(sym) => {
2285 let text = interner.resolve(*sym);
2286 let pv = format!("e_{}", *counter);
2287 *counter += 1;
2288 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", pv, text));
2289 part_vars.push(pv);
2290 }
2291 StringPart::Expr { value, .. } => {
2292 let pv = encode_expr_src(value, counter, output, interner, variants);
2293 part_vars.push(pv);
2294 }
2295 }
2296 }
2297 if part_vars.len() == 1 {
2298 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
2299 } else {
2300 let mut acc = part_vars[0].clone();
2301 for pv in &part_vars[1..] {
2302 let concat_var = format!("e_{}", *counter);
2303 *counter += 1;
2304 output.push_str(&format!(
2305 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
2306 concat_var, acc, pv
2307 ));
2308 acc = concat_var;
2309 }
2310 output.push_str(&format!("Let {} be {}.\n", var, acc));
2311 }
2312 }
2313 }
2314 Expr::Range { start, end } => {
2315 let start_var = encode_expr_src(start, counter, output, interner, variants);
2316 let end_var = encode_expr_src(end, counter, output, interner, variants);
2317 output.push_str(&format!(
2318 "Let {} be a new CRange with start {} and end {}.\n",
2319 var, start_var, end_var
2320 ));
2321 }
2322 Expr::Slice { collection, start, end } => {
2323 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2324 let start_var = encode_expr_src(start, counter, output, interner, variants);
2325 let end_var = encode_expr_src(end, counter, output, interner, variants);
2326 output.push_str(&format!(
2327 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
2328 var, coll_var, start_var, end_var
2329 ));
2330 }
2331 Expr::Copy { expr } => {
2332 let inner_var = encode_expr_src(expr, counter, output, interner, variants);
2333 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
2334 }
2335 Expr::Contains { collection, value } => {
2336 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2337 let val_var = encode_expr_src(value, counter, output, interner, variants);
2338 output.push_str(&format!(
2339 "Let {} be a new CContains with coll {} and elem {}.\n",
2340 var, coll_var, val_var
2341 ));
2342 }
2343 Expr::Union { left, right } => {
2344 let left_var = encode_expr_src(left, counter, output, interner, variants);
2345 let right_var = encode_expr_src(right, counter, output, interner, variants);
2346 output.push_str(&format!(
2347 "Let {} be a new CUnion with left {} and right {}.\n",
2348 var, left_var, right_var
2349 ));
2350 }
2351 Expr::Intersection { left, right } => {
2352 let left_var = encode_expr_src(left, counter, output, interner, variants);
2353 let right_var = encode_expr_src(right, counter, output, interner, variants);
2354 output.push_str(&format!(
2355 "Let {} be a new CIntersection with left {} and right {}.\n",
2356 var, left_var, right_var
2357 ));
2358 }
2359 Expr::OptionSome { value } => {
2360 let inner_var = encode_expr_src(value, counter, output, interner, variants);
2361 output.push_str(&format!(
2362 "Let {} be a new COptionSome with inner {}.\n",
2363 var, inner_var
2364 ));
2365 }
2366 Expr::OptionNone => {
2367 output.push_str(&format!("Let {} be a new COptionNone.\n", var));
2368 }
2369 Expr::Tuple(elems) => {
2370 let items_var = format!("tupItems_{}", *counter);
2371 *counter += 1;
2372 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2373 for elem in elems {
2374 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
2375 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2376 }
2377 output.push_str(&format!(
2378 "Let {} be a new CTuple with items {}.\n",
2379 var, items_var
2380 ));
2381 }
2382 Expr::Closure { params, body, .. } => {
2383 let params_var = format!("clp_{}", *counter);
2384 *counter += 1;
2385 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2386 let mut param_names = HashSet::new();
2387 for (sym, _) in params {
2388 let name = interner.resolve(*sym);
2389 param_names.insert(name.to_string());
2390 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
2391 }
2392 let body_var = format!("clb_{}", *counter);
2393 *counter += 1;
2394 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2395 match body {
2396 ClosureBody::Expression(e) => {
2397 let ret_expr = encode_expr_src(e, counter, output, interner, variants);
2398 let ret_var = format!("s_{}", *counter);
2399 *counter += 1;
2400 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
2401 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
2402 }
2403 ClosureBody::Block(stmts) => {
2404 for s in stmts.iter() {
2405 let sv = encode_stmt_src(s, counter, output, interner, variants);
2406 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2407 }
2408 }
2409 }
2410 let bound: HashSet<String> = param_names;
2411 let free = collect_free_vars_expr(expr, interner, &bound);
2412 let cap_var = format!("clc_{}", *counter);
2413 *counter += 1;
2414 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
2415 for fv in &free {
2416 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
2417 }
2418 output.push_str(&format!(
2419 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
2420 var, params_var, body_var, cap_var
2421 ));
2422 }
2423 Expr::CallExpr { callee, args } => {
2424 let callee_var = encode_expr_src(callee, counter, output, interner, variants);
2425 let args_var = format!("cea_{}", *counter);
2426 *counter += 1;
2427 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2428 for a in args {
2429 let av = encode_expr_src(a, counter, output, interner, variants);
2430 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2431 }
2432 output.push_str(&format!(
2433 "Let {} be a new CCallExpr with target {} and args {}.\n",
2434 var, callee_var, args_var
2435 ));
2436 }
2437 Expr::Give { value } => {
2438 let inner_var = encode_expr_src(value, counter, output, interner, variants);
2439 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
2440 }
2441 Expr::Escape { code, .. } => {
2442 let code_str = interner.resolve(*code);
2443 output.push_str(&format!(
2444 "Let {} be a new CEscExpr with code \"{}\".\n",
2445 var, code_str.replace('\"', "\\\"")
2446 ));
2447 }
2448 Expr::List(elems) => {
2449 let items_var = format!("litems_{}", *counter);
2450 *counter += 1;
2451 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2452 for elem in elems {
2453 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
2454 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2455 }
2456 output.push_str(&format!(
2457 "Let {} be a new CList with items {}.\n",
2458 var, items_var
2459 ));
2460 }
2461 _ => {
2462 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2463 }
2464 }
2465
2466 var
2467}
2468
2469fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2470 let var = format!("s_{}", *counter);
2471 *counter += 1;
2472
2473 match stmt {
2474 Stmt::Let { var: name, value, .. } => {
2475 let name_str = interner.resolve(*name);
2476 let expr_var = encode_expr_src(value, counter, output, interner, variants);
2477 output.push_str(&format!(
2478 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2479 var, name_str, expr_var
2480 ));
2481 }
2482 Stmt::Set { target, value } => {
2483 let name_str = interner.resolve(*target);
2484 let expr_var = encode_expr_src(value, counter, output, interner, variants);
2485 output.push_str(&format!(
2486 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2487 var, name_str, expr_var
2488 ));
2489 }
2490 Stmt::If { cond, then_block, else_block } => {
2491 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2492 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
2493 let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
2494 let else_var = if let Some(els) = else_block {
2495 let else_stmts: Vec<&Stmt> = els.iter().collect();
2496 encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
2497 } else {
2498 let empty_var = format!("emptyBlock_{}", *counter);
2499 *counter += 1;
2500 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
2501 empty_var
2502 };
2503 output.push_str(&format!(
2504 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2505 var, cond_var, then_var, else_var
2506 ));
2507 }
2508 Stmt::While { cond, body, .. } => {
2509 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2510 let body_stmts: Vec<&Stmt> = body.iter().collect();
2511 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
2512 output.push_str(&format!(
2513 "Let {} be a new CWhile with cond {} and body {}.\n",
2514 var, cond_var, body_var
2515 ));
2516 }
2517 Stmt::Return { value } => {
2518 if let Some(expr) = value {
2519 let expr_var = encode_expr_src(expr, counter, output, interner, variants);
2520 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
2521 } else {
2522 let nothing_var = format!("e_{}", *counter);
2523 *counter += 1;
2524 output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
2525 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
2526 }
2527 }
2528 Stmt::Show { object, .. } => {
2529 let expr_var = encode_expr_src(object, counter, output, interner, variants);
2530 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
2531 }
2532 Stmt::Call { function, args } => {
2533 let fn_name = interner.resolve(*function);
2534 let args_var = format!("callSArgs_{}", *counter);
2535 *counter += 1;
2536 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2537 for arg in args {
2538 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2539 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2540 }
2541 output.push_str(&format!(
2542 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2543 var, fn_name, args_var
2544 ));
2545 }
2546 Stmt::Push { value, collection } => {
2547 let val_var = encode_expr_src(value, counter, output, interner, variants);
2548 let coll_name = extract_ident_name(collection, interner);
2549 output.push_str(&format!(
2550 "Let {} be a new CPush with expr {} and target \"{}\".\n",
2551 var, val_var, coll_name
2552 ));
2553 }
2554 Stmt::SetIndex { collection, index, value } => {
2555 let coll_name = extract_ident_name(collection, interner);
2556 let idx_var = encode_expr_src(index, counter, output, interner, variants);
2557 let val_var = encode_expr_src(value, counter, output, interner, variants);
2558 output.push_str(&format!(
2559 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
2560 var, coll_name, idx_var, val_var
2561 ));
2562 }
2563 Stmt::SetField { object, field, value } => {
2564 let map_name = extract_ident_name(object, interner);
2565 let field_name = interner.resolve(*field);
2566 let key_var = format!("e_{}", *counter);
2567 *counter += 1;
2568 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2569 let val_var = encode_expr_src(value, counter, output, interner, variants);
2570 output.push_str(&format!(
2571 "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
2572 var, map_name, key_var, val_var
2573 ));
2574 }
2575 Stmt::Pop { collection, .. } => {
2576 let coll_name = extract_ident_name(collection, interner);
2577 output.push_str(&format!(
2578 "Let {} be a new CPop with target \"{}\".\n",
2579 var, coll_name
2580 ));
2581 }
2582 Stmt::Add { value, collection } => {
2583 let val_var = encode_expr_src(value, counter, output, interner, variants);
2584 let coll_name = extract_ident_name(collection, interner);
2585 output.push_str(&format!(
2586 "Let {} be a new CAdd with elem {} and target \"{}\".\n",
2587 var, val_var, coll_name
2588 ));
2589 }
2590 Stmt::Remove { value, collection } => {
2591 let val_var = encode_expr_src(value, counter, output, interner, variants);
2592 let coll_name = extract_ident_name(collection, interner);
2593 output.push_str(&format!(
2594 "Let {} be a new CRemove with elem {} and target \"{}\".\n",
2595 var, val_var, coll_name
2596 ));
2597 }
2598 Stmt::Inspect { .. } => {
2599 return String::new(); }
2601 Stmt::Repeat { .. } => {
2602 return String::new(); }
2604 Stmt::Break => {
2605 output.push_str(&format!("Let {} be a new CBreak.\n", var));
2606 }
2607 Stmt::RuntimeAssert { condition, .. } => {
2608 let cond_var = encode_expr_src(condition, counter, output, interner, variants);
2609 let msg_var = format!("e_{}", *counter);
2610 *counter += 1;
2611 output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
2612 output.push_str(&format!(
2613 "Let {} be a new CRuntimeAssert with cond {} and msg {}.\n",
2614 var, cond_var, msg_var
2615 ));
2616 }
2617 Stmt::Give { object, recipient } => {
2618 let expr_var = encode_expr_src(object, counter, output, interner, variants);
2619 let target_name = extract_ident_name(recipient, interner);
2620 output.push_str(&format!(
2621 "Let {} be a new CGive with expr {} and target \"{}\".\n",
2622 var, expr_var, target_name
2623 ));
2624 }
2625 Stmt::Escape { code, .. } => {
2626 let code_str = interner.resolve(*code);
2627 output.push_str(&format!(
2628 "Let {} be a new CEscStmt with code \"{}\".\n",
2629 var, code_str.replace('\"', "\\\"")
2630 ));
2631 }
2632 Stmt::Sleep { milliseconds } => {
2633 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2634 output.push_str(&format!(
2635 "Let {} be a new CSleep with duration {}.\n",
2636 var, dur_var
2637 ));
2638 }
2639 Stmt::ReadFrom { var: read_var, source } => {
2640 let var_name = interner.resolve(*read_var);
2641 match source {
2642 ReadSource::Console => {
2643 output.push_str(&format!(
2644 "Let {} be a new CReadConsole with target \"{}\".\n",
2645 var, var_name
2646 ));
2647 }
2648 ReadSource::File(path_expr) => {
2649 let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
2650 output.push_str(&format!(
2651 "Let {} be a new CReadFile with path {} and target \"{}\".\n",
2652 var, path_var, var_name
2653 ));
2654 }
2655 }
2656 }
2657 Stmt::WriteFile { content, path } => {
2658 let path_var = encode_expr_src(path, counter, output, interner, variants);
2659 let content_var = encode_expr_src(content, counter, output, interner, variants);
2660 output.push_str(&format!(
2661 "Let {} be a new CWriteFile with path {} and content {}.\n",
2662 var, path_var, content_var
2663 ));
2664 }
2665 Stmt::Check { source_text, .. } => {
2666 let pred_var = format!("e_{}", *counter);
2667 *counter += 1;
2668 output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
2669 let msg_var = format!("e_{}", *counter);
2670 *counter += 1;
2671 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
2672 output.push_str(&format!(
2673 "Let {} be a new CCheck with predicate {} and msg {}.\n",
2674 var, pred_var, msg_var
2675 ));
2676 }
2677 Stmt::Assert { .. } => {
2678 let prop_var = format!("e_{}", *counter);
2679 *counter += 1;
2680 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2681 output.push_str(&format!(
2682 "Let {} be a new CAssert with proposition {}.\n",
2683 var, prop_var
2684 ));
2685 }
2686 Stmt::Trust { justification, .. } => {
2687 let prop_var = format!("e_{}", *counter);
2688 *counter += 1;
2689 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2690 let just_str = interner.resolve(*justification);
2691 output.push_str(&format!(
2692 "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
2693 var, prop_var, just_str
2694 ));
2695 }
2696 Stmt::Require { crate_name, .. } => {
2697 let dep_name = interner.resolve(*crate_name);
2698 output.push_str(&format!(
2699 "Let {} be a new CRequire with dependency \"{}\".\n",
2700 var, dep_name
2701 ));
2702 }
2703 Stmt::MergeCrdt { source, target } => {
2704 let source_var = encode_expr_src(source, counter, output, interner, variants);
2705 let target_name = match target {
2706 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2707 _ => "unknown".to_string(),
2708 };
2709 output.push_str(&format!(
2710 "Let {} be a new CMerge with target \"{}\" and other {}.\n",
2711 var, target_name, source_var
2712 ));
2713 }
2714 Stmt::IncreaseCrdt { object, field, amount } => {
2715 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2716 let target_name = match object {
2717 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2718 _ => "unknown".to_string(),
2719 };
2720 output.push_str(&format!(
2721 "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
2722 var, target_name, amount_var
2723 ));
2724 }
2725 Stmt::DecreaseCrdt { object, field, amount } => {
2726 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2727 let target_name = match object {
2728 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2729 _ => "unknown".to_string(),
2730 };
2731 output.push_str(&format!(
2732 "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
2733 var, target_name, amount_var
2734 ));
2735 }
2736 Stmt::AppendToSequence { sequence, value } => {
2737 let value_var = encode_expr_src(value, counter, output, interner, variants);
2738 let target_name = match sequence {
2739 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2740 _ => "unknown".to_string(),
2741 };
2742 output.push_str(&format!(
2743 "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
2744 var, target_name, value_var
2745 ));
2746 }
2747 Stmt::ResolveConflict { object, .. } => {
2748 let target_name = match object {
2749 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2750 _ => "unknown".to_string(),
2751 };
2752 output.push_str(&format!(
2753 "Let {} be a new CResolve with target \"{}\".\n",
2754 var, target_name
2755 ));
2756 }
2757 Stmt::Sync { var: sync_var, topic } => {
2758 let topic_var = encode_expr_src(topic, counter, output, interner, variants);
2759 let var_name = interner.resolve(*sync_var);
2760 output.push_str(&format!(
2761 "Let {} be a new CSync with target \"{}\" and channel {}.\n",
2762 var, var_name, topic_var
2763 ));
2764 }
2765 Stmt::Mount { var: mount_var, path } => {
2766 let path_var = encode_expr_src(path, counter, output, interner, variants);
2767 let var_name = interner.resolve(*mount_var);
2768 output.push_str(&format!(
2769 "Let {} be a new CMount with target \"{}\" and path {}.\n",
2770 var, var_name, path_var
2771 ));
2772 }
2773 Stmt::Concurrent { tasks } => {
2774 let branches_var = format!("e_{}", *counter);
2775 *counter += 1;
2776 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2777 let branch_var = format!("e_{}", *counter);
2778 *counter += 1;
2779 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2780 for stmt in tasks.iter() {
2781 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2782 if !sv.is_empty() {
2783 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2784 }
2785 }
2786 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2787 output.push_str(&format!(
2788 "Let {} be a new CConcurrent with branches {}.\n",
2789 var, branches_var
2790 ));
2791 }
2792 Stmt::Parallel { tasks } => {
2793 let branches_var = format!("e_{}", *counter);
2794 *counter += 1;
2795 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2796 let branch_var = format!("e_{}", *counter);
2797 *counter += 1;
2798 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2799 for stmt in tasks.iter() {
2800 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2801 if !sv.is_empty() {
2802 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2803 }
2804 }
2805 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2806 output.push_str(&format!(
2807 "Let {} be a new CParallel with branches {}.\n",
2808 var, branches_var
2809 ));
2810 }
2811 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
2812 let func_name = interner.resolve(*function);
2813 let args_var = format!("e_{}", *counter);
2814 *counter += 1;
2815 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2816 for arg in args {
2817 let av = encode_expr_src(arg, counter, output, interner, variants);
2818 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2819 }
2820 let body_var = format!("e_{}", *counter);
2821 *counter += 1;
2822 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2823 let call_var = format!("e_{}", *counter);
2824 *counter += 1;
2825 output.push_str(&format!(
2826 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2827 call_var, func_name, args_var
2828 ));
2829 output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
2830 let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
2831 interner.resolve(*handle).to_string()
2832 } else {
2833 "_task".to_string()
2834 };
2835 output.push_str(&format!(
2836 "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
2837 var, body_var, handle_name
2838 ));
2839 }
2840 Stmt::StopTask { handle } => {
2841 let handle_var = encode_expr_src(handle, counter, output, interner, variants);
2842 output.push_str(&format!(
2843 "Let {} be a new CStopTask with handle {}.\n",
2844 var, handle_var
2845 ));
2846 }
2847 Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
2848 let cap = capacity.unwrap_or(32);
2849 let cap_var = format!("e_{}", *counter);
2850 *counter += 1;
2851 output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
2852 let pipe_name = interner.resolve(*pipe_var);
2853 output.push_str(&format!(
2854 "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
2855 var, pipe_name, cap_var
2856 ));
2857 }
2858 Stmt::SendPipe { value, pipe } => {
2859 let val_var = encode_expr_src(value, counter, output, interner, variants);
2860 let pipe_name = match pipe {
2861 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2862 _ => "pipe".to_string(),
2863 };
2864 output.push_str(&format!(
2865 "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
2866 var, pipe_name, val_var
2867 ));
2868 }
2869 Stmt::ReceivePipe { var: recv_var, pipe } => {
2870 let recv_name = interner.resolve(*recv_var);
2871 let pipe_name = match pipe {
2872 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2873 _ => "pipe".to_string(),
2874 };
2875 output.push_str(&format!(
2876 "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
2877 var, pipe_name, recv_name
2878 ));
2879 }
2880 Stmt::TrySendPipe { value, pipe, .. } => {
2881 let val_var = encode_expr_src(value, counter, output, interner, variants);
2882 let pipe_name = match pipe {
2883 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2884 _ => "pipe".to_string(),
2885 };
2886 output.push_str(&format!(
2887 "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
2888 var, pipe_name, val_var
2889 ));
2890 }
2891 Stmt::TryReceivePipe { var: recv_var, pipe } => {
2892 let recv_name = interner.resolve(*recv_var);
2893 let pipe_name = match pipe {
2894 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2895 _ => "pipe".to_string(),
2896 };
2897 output.push_str(&format!(
2898 "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
2899 var, pipe_name, recv_name
2900 ));
2901 }
2902 Stmt::Select { branches } => {
2903 let branches_var = format!("e_{}", *counter);
2904 *counter += 1;
2905 output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
2906 for branch in branches {
2907 match branch {
2908 SelectBranch::Receive { var: recv_var, pipe, body } => {
2909 let recv_name = interner.resolve(*recv_var);
2910 let pipe_name = match pipe {
2911 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2912 _ => "pipe".to_string(),
2913 };
2914 let body_var = format!("e_{}", *counter);
2915 *counter += 1;
2916 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2917 for stmt in body.iter() {
2918 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2919 if !sv.is_empty() {
2920 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2921 }
2922 }
2923 let branch_var = format!("e_{}", *counter);
2924 *counter += 1;
2925 output.push_str(&format!(
2926 "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
2927 branch_var, pipe_name, recv_name, body_var
2928 ));
2929 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2930 }
2931 SelectBranch::Timeout { milliseconds, body } => {
2932 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2933 let body_var = format!("e_{}", *counter);
2934 *counter += 1;
2935 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2936 for stmt in body.iter() {
2937 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2938 if !sv.is_empty() {
2939 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2940 }
2941 }
2942 let branch_var = format!("e_{}", *counter);
2943 *counter += 1;
2944 output.push_str(&format!(
2945 "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
2946 branch_var, dur_var, body_var
2947 ));
2948 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2949 }
2950 }
2951 }
2952 output.push_str(&format!(
2953 "Let {} be a new CSelect with branches {}.\n",
2954 var, branches_var
2955 ));
2956 }
2957 Stmt::Spawn { agent_type, name } => {
2958 let agent_name = interner.resolve(*agent_type);
2959 let target_name = interner.resolve(*name);
2960 output.push_str(&format!(
2961 "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
2962 var, agent_name, target_name
2963 ));
2964 }
2965 Stmt::SendMessage { message, destination } => {
2966 let target_var = encode_expr_src(destination, counter, output, interner, variants);
2967 let msg_var = encode_expr_src(message, counter, output, interner, variants);
2968 output.push_str(&format!(
2969 "Let {} be a new CSendMessage with target {} and msg {}.\n",
2970 var, target_var, msg_var
2971 ));
2972 }
2973 Stmt::AwaitMessage { into, .. } => {
2974 let await_name = interner.resolve(*into);
2975 output.push_str(&format!(
2976 "Let {} be a new CAwaitMessage with target \"{}\".\n",
2977 var, await_name
2978 ));
2979 }
2980 Stmt::Listen { address } => {
2981 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2982 output.push_str(&format!(
2983 "Let {} be a new CListen with addr {} and handler \"default\".\n",
2984 var, addr_var
2985 ));
2986 }
2987 Stmt::ConnectTo { address } => {
2988 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2989 output.push_str(&format!(
2990 "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
2991 var, addr_var
2992 ));
2993 }
2994 Stmt::Zone { name, body, .. } => {
2995 let zone_name = interner.resolve(*name);
2996 let body_var = format!("e_{}", *counter);
2997 *counter += 1;
2998 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2999 for stmt in body.iter() {
3000 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
3001 if !sv.is_empty() {
3002 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
3003 }
3004 }
3005 output.push_str(&format!(
3006 "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
3007 var, zone_name, body_var
3008 ));
3009 }
3010 Stmt::LetPeerAgent { var: pa_var, address } => {
3011 let addr_var = encode_expr_src(address, counter, output, interner, variants);
3012 let pa_name = interner.resolve(*pa_var);
3013 output.push_str(&format!(
3014 "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
3015 var, addr_var, pa_name
3016 ));
3017 }
3018 _ => {
3019 return String::new();
3020 }
3021 }
3022
3023 var
3024}
3025
3026fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
3027 match stmt {
3028 Stmt::Inspect { target, arms, .. } => {
3029 let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
3030 let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
3031
3032 for arm in arms {
3033 if arm.variant.is_none() {
3034 otherwise_stmts = arm.body.iter().collect();
3035 } else {
3036 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
3037 variant_arms.push((arm, body_refs));
3038 }
3039 }
3040
3041 if variant_arms.is_empty() {
3042 let mut result = Vec::new();
3043 for s in &otherwise_stmts {
3044 for v in encode_stmts_src(s, counter, output, interner, variants) {
3045 result.push(v);
3046 }
3047 }
3048 return result;
3049 }
3050
3051 let has_otherwise = !otherwise_stmts.is_empty();
3056 let mut result = Vec::new();
3057
3058 let matched_var_name = if has_otherwise {
3060 let name = format!("__inspectMatched_{}", *counter);
3061 *counter += 1;
3062 let false_expr = format!("e_{}", *counter);
3063 *counter += 1;
3064 output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
3065 let let_stmt = format!("s_{}", *counter);
3066 *counter += 1;
3067 output.push_str(&format!(
3068 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3069 let_stmt, name, false_expr
3070 ));
3071 result.push(let_stmt);
3072 Some(name)
3073 } else {
3074 None
3075 };
3076
3077 for (arm, body_stmts) in &variant_arms {
3079 let variant_name = interner.resolve(arm.variant.unwrap());
3080
3081 let tag_target = encode_expr_src(target, counter, output, interner, variants);
3083 let tag_key = format!("e_{}", *counter);
3084 *counter += 1;
3085 output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
3086 let tag_get = format!("e_{}", *counter);
3087 *counter += 1;
3088 output.push_str(&format!(
3089 "Let {} be a new CMapGet with target {} and key {}.\n",
3090 tag_get, tag_target, tag_key
3091 ));
3092 let variant_text = format!("e_{}", *counter);
3093 *counter += 1;
3094 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
3095 let cond_var = format!("e_{}", *counter);
3096 *counter += 1;
3097 output.push_str(&format!(
3098 "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
3099 cond_var, tag_get, variant_text
3100 ));
3101
3102 let then_list = format!("stmtList_{}", *counter);
3104 *counter += 1;
3105 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
3106
3107 if let Some(ref mname) = matched_var_name {
3109 let true_expr = format!("e_{}", *counter);
3110 *counter += 1;
3111 output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
3112 let set_stmt = format!("s_{}", *counter);
3113 *counter += 1;
3114 output.push_str(&format!(
3115 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3116 set_stmt, mname, true_expr
3117 ));
3118 output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
3119 }
3120
3121 for (field_name, binding_name) in &arm.bindings {
3123 let field_str = interner.resolve(*field_name);
3124 let bind_str = interner.resolve(*binding_name);
3125 let bind_target = encode_expr_src(target, counter, output, interner, variants);
3126 let fkey = format!("e_{}", *counter);
3127 *counter += 1;
3128 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
3129 let fget = format!("e_{}", *counter);
3130 *counter += 1;
3131 output.push_str(&format!(
3132 "Let {} be a new CMapGet with target {} and key {}.\n",
3133 fget, bind_target, fkey
3134 ));
3135 let bind_let = format!("s_{}", *counter);
3136 *counter += 1;
3137 output.push_str(&format!(
3138 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3139 bind_let, bind_str, fget
3140 ));
3141 output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
3142 }
3143
3144 for body_stmt in body_stmts {
3146 match body_stmt {
3147 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
3148 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
3149 for v in vars {
3150 output.push_str(&format!("Push {} to {}.\n", v, then_list));
3151 }
3152 }
3153 _ => {
3154 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
3155 if !bvar.is_empty() {
3156 output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
3157 }
3158 }
3159 }
3160 }
3161
3162 let empty_else = format!("stmtList_{}", *counter);
3164 *counter += 1;
3165 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3166
3167 let if_var = format!("s_{}", *counter);
3169 *counter += 1;
3170 output.push_str(&format!(
3171 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3172 if_var, cond_var, then_list, empty_else
3173 ));
3174
3175 result.push(if_var);
3176 }
3177
3178 if let Some(ref mname) = matched_var_name {
3180 let matched_ref = format!("e_{}", *counter);
3181 *counter += 1;
3182 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
3183 let not_matched = format!("e_{}", *counter);
3184 *counter += 1;
3185 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
3186
3187 let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
3188 let empty_else = format!("stmtList_{}", *counter);
3189 *counter += 1;
3190 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3191
3192 let otherwise_if = format!("s_{}", *counter);
3193 *counter += 1;
3194 output.push_str(&format!(
3195 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3196 otherwise_if, not_matched, otherwise_block, empty_else
3197 ));
3198 result.push(otherwise_if);
3199 }
3200
3201 result
3202 }
3203 Stmt::Repeat { pattern, iterable, body, .. } => {
3204 let loop_var_name = match pattern {
3205 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3206 Pattern::Tuple(syms) => {
3207 if let Some(s) = syms.first() {
3208 interner.resolve(*s).to_string()
3209 } else {
3210 "item".to_string()
3211 }
3212 }
3213 };
3214
3215 if let Expr::Range { start, end } = iterable {
3217 let start_var = encode_expr_src(start, counter, output, interner, variants);
3218 let end_var = encode_expr_src(end, counter, output, interner, variants);
3219 let body_stmts: Vec<&Stmt> = body.iter().collect();
3220 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
3221 let rr = format!("s_{}", *counter);
3222 *counter += 1;
3223 output.push_str(&format!(
3224 "Let {} be a new CRepeatRange with var \"{}\" and start {} and end {} and body {}.\n",
3225 rr, loop_var_name, start_var, end_var, body_var
3226 ));
3227 return vec![rr];
3228 }
3229
3230 let coll_var = encode_expr_src(iterable, counter, output, interner, variants);
3232 let body_stmts: Vec<&Stmt> = body.iter().collect();
3233 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
3234 let rep = format!("s_{}", *counter);
3235 *counter += 1;
3236 output.push_str(&format!(
3237 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
3238 rep, loop_var_name, coll_var, body_var
3239 ));
3240 vec![rep]
3241 }
3242 _ => {
3243 let v = encode_stmt_src(stmt, counter, output, interner, variants);
3244 if v.is_empty() {
3245 vec![]
3246 } else {
3247 vec![v]
3248 }
3249 }
3250 }
3251}
3252
3253fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3254 let list_var = format!("stmtList_{}", *counter);
3255 *counter += 1;
3256 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
3257
3258 for stmt in stmts {
3259 for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
3260 output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
3261 }
3262 }
3263
3264 list_var
3265}
3266
3267fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
3268 match expr {
3269 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3270 _ => "unknown".to_string(),
3271 }
3272}
3273
3274pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
3287 let full_source = if program.contains("## Main") || program.contains("## To ") {
3288 program.to_string()
3289 } else {
3290 format!("## Main\n{}", program)
3291 };
3292
3293 let mut interner = Interner::new();
3294 let mut lexer = Lexer::new(&full_source, &mut interner);
3295 let tokens = lexer.tokenize();
3296
3297 let type_registry = {
3298 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3299 let result = discovery.run_full();
3300 result.types
3301 };
3302
3303 let mut world_state = WorldState::new();
3304 let expr_arena = Arena::new();
3305 let term_arena = Arena::new();
3306 let np_arena = Arena::new();
3307 let sym_arena = Arena::new();
3308 let role_arena = Arena::new();
3309 let pp_arena = Arena::new();
3310 let stmt_arena: Arena<Stmt> = Arena::new();
3311 let imperative_expr_arena: Arena<Expr> = Arena::new();
3312 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3313
3314 let ast_ctx = AstContext::with_types(
3315 &expr_arena, &term_arena, &np_arena, &sym_arena,
3316 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3317 &type_expr_arena,
3318 );
3319
3320 let mut parser = crate::parser::Parser::new(
3321 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3322 );
3323 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3324
3325 let optimized = crate::optimize::optimize_for_projection(
3330 stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
3331 );
3332
3333 let mut output = String::new();
3334
3335 for stmt in &optimized {
3336 if matches!(stmt, Stmt::FunctionDef { .. }) {
3337 decompile_stmt(stmt, &interner, &mut output, 0);
3338 output.push('\n');
3339 }
3340 }
3341
3342 output.push_str("## Main\n");
3343 for stmt in &optimized {
3344 if !matches!(stmt, Stmt::FunctionDef { .. }) {
3345 decompile_stmt(stmt, &interner, &mut output, 0);
3346 }
3347 }
3348
3349 Ok(output)
3350}
3351
3352fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
3353 let pad = " ".repeat(indent);
3354 match stmt {
3355 Stmt::FunctionDef { name, params, body, return_type, .. } => {
3356 let fn_name = interner.resolve(*name);
3357 let param_strs: Vec<String> = params
3358 .iter()
3359 .map(|(name, ty)| {
3360 let pname = interner.resolve(*name);
3361 format!("{}: {}", pname, decompile_type_expr(ty, interner))
3362 })
3363 .collect();
3364 let ret_str = if let Some(rt) = return_type {
3365 format!(" -> {}", decompile_type_expr(rt, interner))
3366 } else {
3367 String::new()
3368 };
3369 out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
3370 for s in body.iter() {
3371 decompile_stmt(s, interner, out, indent + 1);
3372 }
3373 }
3374 Stmt::Let { var, value, mutable, .. } => {
3375 let name = interner.resolve(*var);
3376 let expr_str = decompile_expr(value, interner);
3377 if *mutable {
3378 out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
3379 } else {
3380 out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
3381 }
3382 }
3383 Stmt::Set { target, value } => {
3384 let name = interner.resolve(*target);
3385 let expr_str = decompile_expr(value, interner);
3386 out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
3387 }
3388 Stmt::Show { object, .. } => {
3389 let expr_str = decompile_expr(object, interner);
3390 out.push_str(&format!("{}Show {}.\n", pad, expr_str));
3391 }
3392 Stmt::Return { value } => {
3393 if let Some(expr) = value {
3394 let expr_str = decompile_expr(expr, interner);
3395 out.push_str(&format!("{}Return {}.\n", pad, expr_str));
3396 } else {
3397 out.push_str(&format!("{}Return.\n", pad));
3398 }
3399 }
3400 Stmt::If { cond, then_block, else_block } => {
3401 let cond_str = decompile_expr(cond, interner);
3402 out.push_str(&format!("{}If {}:\n", pad, cond_str));
3403 for s in then_block.iter() {
3404 decompile_stmt(s, interner, out, indent + 1);
3405 }
3406 if let Some(els) = else_block {
3407 out.push_str(&format!("{}Otherwise:\n", pad));
3408 for s in els.iter() {
3409 decompile_stmt(s, interner, out, indent + 1);
3410 }
3411 }
3412 }
3413 Stmt::While { cond, body, .. } => {
3414 let cond_str = decompile_expr(cond, interner);
3415 out.push_str(&format!("{}While {}:\n", pad, cond_str));
3416 for s in body.iter() {
3417 decompile_stmt(s, interner, out, indent + 1);
3418 }
3419 }
3420 Stmt::Call { function, args } => {
3421 let fn_name = interner.resolve(*function);
3422 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3423 if arg_strs.is_empty() {
3424 out.push_str(&format!("{}{}().\n", pad, fn_name));
3425 } else {
3426 out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
3427 }
3428 }
3429 Stmt::Push { value, collection } => {
3430 let val_str = decompile_expr(value, interner);
3431 let coll_str = decompile_expr(collection, interner);
3432 out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
3433 }
3434 Stmt::SetIndex { collection, index, value } => {
3435 let coll_str = decompile_expr(collection, interner);
3436 let idx_str = decompile_expr(index, interner);
3437 let val_str = decompile_expr(value, interner);
3438 out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
3439 }
3440 Stmt::SetField { object, field, value } => {
3441 let obj_str = decompile_expr(object, interner);
3442 let field_name = interner.resolve(*field);
3443 let val_str = decompile_expr(value, interner);
3444 out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
3445 }
3446 Stmt::Repeat { pattern, iterable, body, .. } => {
3447 let var_name = match pattern {
3448 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3449 Pattern::Tuple(syms) => {
3450 syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
3451 }
3452 };
3453 let iter_str = decompile_expr(iterable, interner);
3454 out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
3455 for s in body.iter() {
3456 decompile_stmt(s, interner, out, indent + 1);
3457 }
3458 }
3459 Stmt::Inspect { target, arms, .. } => {
3460 let target_str = decompile_expr(target, interner);
3461 out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
3462 for arm in arms {
3463 if let Some(variant) = arm.variant {
3464 let variant_name = interner.resolve(variant);
3465 let bindings: Vec<String> = arm.bindings.iter()
3466 .map(|(_, b)| interner.resolve(*b).to_string())
3467 .collect();
3468 if bindings.is_empty() {
3469 out.push_str(&format!("{} When {}:\n", pad, variant_name));
3470 } else {
3471 out.push_str(&format!("{} When {}({}):\n", pad, variant_name, bindings.join(", ")));
3472 }
3473 } else {
3474 out.push_str(&format!("{} Otherwise:\n", pad));
3475 }
3476 for s in arm.body.iter() {
3477 decompile_stmt(s, interner, out, indent + 2);
3478 }
3479 }
3480 }
3481 Stmt::Pop { collection, into } => {
3482 let coll_str = decompile_expr(collection, interner);
3483 if let Some(target) = into {
3484 let target_name = interner.resolve(*target);
3485 out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
3486 } else {
3487 out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
3488 }
3489 }
3490 Stmt::Break => {
3491 out.push_str(&format!("{}Break.\n", pad));
3492 }
3493 Stmt::RuntimeAssert { condition } => {
3494 let cond_str = decompile_expr(condition, interner);
3495 out.push_str(&format!("{}Assert that {}.\n", pad, cond_str));
3496 }
3497 Stmt::Add { value, collection } => {
3498 let val_str = decompile_expr(value, interner);
3499 let coll_str = decompile_expr(collection, interner);
3500 out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
3501 }
3502 Stmt::Remove { value, collection } => {
3503 let val_str = decompile_expr(value, interner);
3504 let coll_str = decompile_expr(collection, interner);
3505 out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
3506 }
3507 Stmt::Zone { name, body, .. } => {
3508 let zone_name = interner.resolve(*name);
3509 out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
3510 for s in body.iter() {
3511 decompile_stmt(s, interner, out, indent + 1);
3512 }
3513 }
3514 Stmt::ReadFrom { var, .. } => {
3515 let var_name = interner.resolve(*var);
3516 out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
3517 }
3518 Stmt::WriteFile { content, path } => {
3519 let content_str = decompile_expr(content, interner);
3520 let path_str = decompile_expr(path, interner);
3521 out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
3522 }
3523 Stmt::Sleep { milliseconds } => {
3524 let ms = decompile_expr(milliseconds, interner);
3525 out.push_str(&format!("{}Sleep {}.\n", pad, ms));
3526 }
3527 _ => {
3528 }
3531 }
3532}
3533
3534fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
3535 match expr {
3536 Expr::Literal(lit) => match lit {
3537 Literal::Number(n) => n.to_string(),
3538 Literal::Float(f) => format!("{}", f),
3539 Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
3540 Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
3541 Literal::Nothing => "nothing".to_string(),
3542 Literal::Char(c) => format!("'{}'", c),
3543 Literal::Duration(ns) => format!("{}", ns),
3544 Literal::Date(days) => format!("{}", days),
3545 Literal::Moment(ns) => format!("{}", ns),
3546 Literal::Span { months, days } => format!("{} months {} days", months, days),
3547 Literal::Time(ns) => format!("{}", ns),
3548 },
3549 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3550 Expr::BinaryOp { op, left, right } => {
3551 let l = if matches!(left, Expr::BinaryOp { .. }) {
3552 format!("({})", decompile_expr(left, interner))
3553 } else {
3554 decompile_expr(left, interner)
3555 };
3556 let r = if matches!(right, Expr::BinaryOp { .. }) {
3557 format!("({})", decompile_expr(right, interner))
3558 } else {
3559 decompile_expr(right, interner)
3560 };
3561 if matches!(op, BinaryOpKind::Shl) {
3564 if let Expr::Literal(Literal::Number(k)) = right {
3566 let multiplier = 1i64 << k;
3567 return format!("{} * {}", l, multiplier);
3568 }
3569 }
3570 if matches!(op, BinaryOpKind::Shr) {
3571 if let Expr::Literal(Literal::Number(k)) = right {
3573 let divisor = 1i64 << k;
3574 return format!("{} / {}", l, divisor);
3575 }
3576 }
3577 let op_str = match op {
3578 BinaryOpKind::Add => "+",
3579 BinaryOpKind::Subtract => "-",
3580 BinaryOpKind::Multiply => "*",
3581 BinaryOpKind::Divide => "/",
3582 BinaryOpKind::Modulo => "%",
3583 BinaryOpKind::Eq => "equals",
3584 BinaryOpKind::NotEq => "is not",
3585 BinaryOpKind::Lt => "is less than",
3586 BinaryOpKind::Gt => "is greater than",
3587 BinaryOpKind::LtEq => "is at most",
3588 BinaryOpKind::GtEq => "is at least",
3589 BinaryOpKind::And => "and",
3590 BinaryOpKind::Or => "or",
3591 BinaryOpKind::Concat => "+",
3592 BinaryOpKind::BitXor => "+",
3593 BinaryOpKind::Shl => "*",
3594 BinaryOpKind::Shr => "/",
3595 };
3596 format!("{} {} {}", l, op_str, r)
3597 }
3598 Expr::Not { operand } => {
3599 let inner = decompile_expr(operand, interner);
3600 format!("not {}", inner)
3601 }
3602 Expr::Call { function, args } => {
3603 let fn_name = interner.resolve(*function);
3604 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3605 if arg_strs.is_empty() {
3606 format!("{}()", fn_name)
3607 } else {
3608 format!("{}({})", fn_name, arg_strs.join(", "))
3609 }
3610 }
3611 Expr::Index { collection, index } => {
3612 let coll = decompile_expr(collection, interner);
3613 let idx = decompile_expr(index, interner);
3614 format!("item {} of {}", idx, coll)
3615 }
3616 Expr::Length { collection } => {
3617 let coll = decompile_expr(collection, interner);
3618 format!("length of {}", coll)
3619 }
3620 Expr::FieldAccess { object, field } => {
3621 let obj = decompile_expr(object, interner);
3622 let field_name = interner.resolve(*field);
3623 format!("{} of {}", field_name, obj)
3624 }
3625 Expr::New { type_name, .. } => {
3626 let tn = interner.resolve(*type_name);
3627 format!("a new {}", tn)
3628 }
3629 Expr::NewVariant { variant, fields, .. } => {
3630 let vn = interner.resolve(*variant);
3631 if fields.is_empty() {
3632 format!("a new {}", vn)
3633 } else {
3634 let parts: Vec<String> = fields.iter().map(|(name, val)| {
3635 let n = interner.resolve(*name);
3636 let v = decompile_expr(val, interner);
3637 format!("{} {}", n, v)
3638 }).collect();
3639 format!("a new {} with {}", vn, parts.join(" and "))
3640 }
3641 }
3642 Expr::InterpolatedString(parts) => {
3643 let mut result = String::new();
3644 for part in parts {
3645 match part {
3646 StringPart::Literal(sym) => {
3647 result.push_str(&interner.resolve(*sym));
3648 }
3649 StringPart::Expr { value, debug, .. } => {
3650 let expr_str = decompile_expr(value, interner);
3651 if *debug {
3652 result.push_str(&format!("{{{}=}}", expr_str));
3653 } else {
3654 result.push_str(&format!("{{{}}}", expr_str));
3655 }
3656 }
3657 }
3658 }
3659 format!("\"{}\"", result)
3660 }
3661 Expr::Slice { collection, start, end } => {
3662 let coll = decompile_expr(collection, interner);
3663 let s = decompile_expr(start, interner);
3664 let e = decompile_expr(end, interner);
3665 format!("{} {} through {}", coll, s, e)
3666 }
3667 Expr::Copy { expr } => {
3668 let inner = decompile_expr(expr, interner);
3669 format!("copy of {}", inner)
3670 }
3671 Expr::Give { value } => {
3672 let inner = decompile_expr(value, interner);
3673 format!("Give {}", inner)
3674 }
3675 Expr::Contains { collection, value } => {
3676 let coll = decompile_expr(collection, interner);
3677 let val = decompile_expr(value, interner);
3678 format!("{} contains {}", coll, val)
3679 }
3680 Expr::Union { left, right } => {
3681 let l = decompile_expr(left, interner);
3682 let r = decompile_expr(right, interner);
3683 format!("{} union {}", l, r)
3684 }
3685 Expr::Intersection { left, right } => {
3686 let l = decompile_expr(left, interner);
3687 let r = decompile_expr(right, interner);
3688 format!("{} intersection {}", l, r)
3689 }
3690 Expr::List(elems) => {
3691 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3692 format!("[{}]", parts.join(", "))
3693 }
3694 Expr::Tuple(elems) => {
3695 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3696 format!("({})", parts.join(", "))
3697 }
3698 Expr::Range { start, end } => {
3699 let s = decompile_expr(start, interner);
3700 let e = decompile_expr(end, interner);
3701 format!("{} to {}", s, e)
3702 }
3703 Expr::OptionSome { value } => {
3704 let inner = decompile_expr(value, interner);
3705 format!("some {}", inner)
3706 }
3707 Expr::OptionNone => "none".to_string(),
3708 Expr::WithCapacity { value, capacity } => {
3709 let val = decompile_expr(value, interner);
3710 let cap = decompile_expr(capacity, interner);
3711 format!("{} with capacity {}", val, cap)
3712 }
3713 Expr::Escape { language, code } => {
3714 let lang = interner.resolve(*language);
3715 let src = interner.resolve(*code);
3716 format!("Escape to {}:\n{}", lang, src)
3717 }
3718 Expr::ManifestOf { zone } => {
3719 let z = decompile_expr(zone, interner);
3720 format!("the manifest of {}", z)
3721 }
3722 Expr::ChunkAt { index, zone } => {
3723 let idx = decompile_expr(index, interner);
3724 let z = decompile_expr(zone, interner);
3725 format!("the chunk at {} in {}", idx, z)
3726 }
3727 Expr::Closure { params, body, return_type } => {
3728 let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
3729 let n = interner.resolve(*name);
3730 let t = decompile_type_expr(ty, interner);
3731 format!("{}: {}", n, t)
3732 }).collect();
3733 let ret = if let Some(rt) = return_type {
3734 format!(" -> {}", decompile_type_expr(rt, interner))
3735 } else {
3736 String::new()
3737 };
3738 match body {
3739 ClosureBody::Expression(expr) => {
3740 let e = decompile_expr(expr, interner);
3741 format!("({}){} -> {}", param_strs.join(", "), ret, e)
3742 }
3743 ClosureBody::Block(_) => {
3744 format!("({}){} -> [block]", param_strs.join(", "), ret)
3745 }
3746 }
3747 }
3748 Expr::CallExpr { callee, args } => {
3749 let c = decompile_expr(callee, interner);
3750 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3751 format!("{}({})", c, arg_strs.join(", "))
3752 }
3753 }
3754}
3755
3756fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
3757 match ty {
3758 TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
3759 TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
3760 TypeExpr::Generic { base, params } => {
3761 let base_str = interner.resolve(*base);
3762 let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
3763 format!("{} of {}", base_str, param_strs.join(" and "))
3764 }
3765 TypeExpr::Function { inputs, output } => {
3766 let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
3767 let out_str = decompile_type_expr(output, interner);
3768 format!("fn({}) -> {}", in_strs.join(", "), out_str)
3769 }
3770 TypeExpr::Refinement { base, .. } => {
3771 decompile_type_expr(base, interner)
3772 }
3773 TypeExpr::Persistent { inner } => {
3774 format!("Persistent {}", decompile_type_expr(inner, interner))
3775 }
3776 }
3777}
3778
3779pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
3786 let mut interner = Interner::new();
3787 let mut lexer = Lexer::new(source, &mut interner);
3788 let tokens = lexer.tokenize();
3789
3790 let type_registry = {
3791 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3792 let result = discovery.run_full();
3793 result.types
3794 };
3795
3796 let mut world_state = WorldState::new();
3797 let expr_arena = Arena::new();
3798 let term_arena = Arena::new();
3799 let np_arena = Arena::new();
3800 let sym_arena = Arena::new();
3801 let role_arena = Arena::new();
3802 let pp_arena = Arena::new();
3803 let stmt_arena: Arena<Stmt> = Arena::new();
3804 let imperative_expr_arena: Arena<Expr> = Arena::new();
3805 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3806
3807 let ast_ctx = AstContext::with_types(
3808 &expr_arena, &term_arena, &np_arena, &sym_arena,
3809 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3810 &type_expr_arena,
3811 );
3812
3813 let mut parser = crate::parser::Parser::new(
3814 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3815 );
3816 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3817
3818 verify_no_overhead_stmts(&stmts, &interner)
3819}
3820
3821const CORE_VARIANT_NAMES: &[&str] = &[
3822 "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
3823 "CCall", "CIndex", "CLen", "CMapGet",
3824 "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
3825 "CCallS", "CPush", "CSetIdx", "CMapSet",
3826 "CFuncDef", "CProg",
3827 "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
3828];
3829
3830fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
3831 for stmt in stmts {
3832 check_stmt_overhead(stmt, interner)?;
3833 }
3834 Ok(())
3835}
3836
3837fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
3838 match stmt {
3839 Stmt::Inspect { arms, .. } => {
3840 for arm in arms {
3841 if let Some(variant) = arm.variant {
3842 let variant_name = interner.resolve(variant);
3843 if CORE_VARIANT_NAMES.contains(&variant_name) {
3844 return Err(format!(
3845 "Interpretive overhead: Inspect dispatches on Core variant '{}'",
3846 variant_name
3847 ));
3848 }
3849 }
3850 for s in arm.body.iter() {
3851 check_stmt_overhead(s, interner)?;
3852 }
3853 }
3854 }
3855 Stmt::If { cond, then_block, else_block } => {
3856 check_expr_overhead(cond, interner)?;
3857 for s in then_block.iter() {
3858 check_stmt_overhead(s, interner)?;
3859 }
3860 if let Some(els) = else_block {
3861 for s in els.iter() {
3862 check_stmt_overhead(s, interner)?;
3863 }
3864 }
3865 }
3866 Stmt::While { cond, body, .. } => {
3867 check_expr_overhead(cond, interner)?;
3868 for s in body.iter() {
3869 check_stmt_overhead(s, interner)?;
3870 }
3871 }
3872 Stmt::FunctionDef { body, .. } => {
3873 for s in body.iter() {
3874 check_stmt_overhead(s, interner)?;
3875 }
3876 }
3877 Stmt::Repeat { body, .. } => {
3878 for s in body.iter() {
3879 check_stmt_overhead(s, interner)?;
3880 }
3881 }
3882 Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
3883 check_expr_overhead(value, interner)?;
3884 }
3885 Stmt::Return { value } => {
3886 if let Some(v) = value {
3887 check_expr_overhead(v, interner)?;
3888 }
3889 }
3890 _ => {}
3891 }
3892 Ok(())
3893}
3894
3895fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
3896 match expr {
3897 Expr::Index { collection, index } => {
3898 if let Expr::Identifier(coll_sym) = collection {
3900 let coll_name = interner.resolve(*coll_sym);
3901 if coll_name == "env" {
3902 if let Expr::Literal(Literal::Text(_)) = index {
3903 return Err(
3904 "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
3905 );
3906 }
3907 }
3908 }
3909 check_expr_overhead(collection, interner)?;
3910 check_expr_overhead(index, interner)?;
3911 }
3912 Expr::New { type_name, .. } => {
3913 let tn = interner.resolve(*type_name);
3914 if CORE_VARIANT_NAMES.contains(&tn) {
3915 return Err(format!(
3916 "Interpretive overhead: Core type constructor 'new {}'", tn
3917 ));
3918 }
3919 }
3920 Expr::NewVariant { variant, .. } => {
3921 let vn = interner.resolve(*variant);
3922 if CORE_VARIANT_NAMES.contains(&vn) {
3923 return Err(format!(
3924 "Interpretive overhead: Core variant constructor '{}'", vn
3925 ));
3926 }
3927 }
3928 Expr::Call { function, args } => {
3929 let fn_name = interner.resolve(*function);
3930 if CORE_VARIANT_NAMES.contains(&fn_name) {
3931 return Err(format!(
3932 "Interpretive overhead: Core variant call '{}'", fn_name
3933 ));
3934 }
3935 for a in args {
3936 check_expr_overhead(a, interner)?;
3937 }
3938 }
3939 Expr::BinaryOp { left, right, .. } => {
3940 check_expr_overhead(left, interner)?;
3941 check_expr_overhead(right, interner)?;
3942 }
3943 Expr::Not { operand } => {
3944 check_expr_overhead(operand, interner)?;
3945 }
3946 Expr::Length { collection } => {
3947 check_expr_overhead(collection, interner)?;
3948 }
3949 Expr::FieldAccess { object, .. } => {
3950 check_expr_overhead(object, interner)?;
3951 }
3952 _ => {}
3953 }
3954 Ok(())
3955}
3956
3957pub fn pe_source_text() -> &'static str {
3963 include_str!("optimize/pe_source.logos")
3964}
3965
3966pub fn decompile_source_text() -> &'static str {
3967 include_str!("optimize/decompile_source.logos")
3968}
3969
3970pub fn pe_bti_source_text() -> &'static str {
3971 include_str!("optimize/pe_bti_source.logos")
3972}
3973
3974pub fn pe_mini_source_text() -> &'static str {
3975 include_str!("optimize/pe_mini_source.logos")
3976}
3977
3978const CORE_TYPES_FOR_PE: &str = r#"
3979## A CExpr is one of:
3980 A CInt with value Int.
3981 A CFloat with value Real.
3982 A CBool with value Bool.
3983 A CText with value Text.
3984 A CVar with name Text.
3985 A CBinOp with op Text and left CExpr and right CExpr.
3986 A CNot with inner CExpr.
3987 A CCall with name Text and args Seq of CExpr.
3988 A CIndex with coll CExpr and idx CExpr.
3989 A CLen with target CExpr.
3990 A CMapGet with target CExpr and key CExpr.
3991 A CNewSeq.
3992 A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
3993 A CList with items Seq of CExpr.
3994 A CRange with start CExpr and end CExpr.
3995 A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
3996 A CCopy with target CExpr.
3997 A CNewSet.
3998 A CContains with coll CExpr and elem CExpr.
3999 A CUnion with left CExpr and right CExpr.
4000 A CIntersection with left CExpr and right CExpr.
4001 A COptionSome with inner CExpr.
4002 A COptionNone.
4003 A CTuple with items Seq of CExpr.
4004 A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
4005 A CFieldAccess with target CExpr and field Text.
4006 A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
4007 A CCallExpr with target CExpr and args Seq of CExpr.
4008 A CInterpolatedString with parts Seq of CStringPart.
4009 A CDuration with amount CExpr and unit Text.
4010 A CTimeNow.
4011 A CDateToday.
4012 A CEscExpr with code Text.
4013
4014## A CStringPart is one of:
4015 A CLiteralPart with value Text.
4016 A CExprPart with expr CExpr.
4017
4018## A CStmt is one of:
4019 A CLet with name Text and expr CExpr.
4020 A CSet with name Text and expr CExpr.
4021 A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
4022 A CWhile with cond CExpr and body Seq of CStmt.
4023 A CReturn with expr CExpr.
4024 A CShow with expr CExpr.
4025 A CCallS with name Text and args Seq of CExpr.
4026 A CPush with expr CExpr and target Text.
4027 A CSetIdx with target Text and idx CExpr and val CExpr.
4028 A CMapSet with target Text and key CExpr and val CExpr.
4029 A CPop with target Text.
4030 A CRepeat with var Text and coll CExpr and body Seq of CStmt.
4031 A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
4032 A CBreak.
4033 A CAdd with elem CExpr and target Text.
4034 A CRemove with elem CExpr and target Text.
4035 A CSetField with target Text and field Text and val CExpr.
4036 A CStructDef with name Text and fieldNames Seq of Text.
4037 A CInspect with target CExpr and arms Seq of CMatchArm.
4038 A CEnumDef with name Text and variants Seq of Text.
4039 A CRuntimeAssert with cond CExpr and msg CExpr.
4040 A CGive with expr CExpr and target Text.
4041 A CEscStmt with code Text.
4042 A CSleep with duration CExpr.
4043 A CReadConsole with target Text.
4044 A CReadFile with path CExpr and target Text.
4045 A CWriteFile with path CExpr and content CExpr.
4046 A CCheck with predicate CExpr and msg CExpr.
4047 A CAssert with proposition CExpr.
4048 A CTrust with proposition CExpr and justification Text.
4049 A CRequire with dependency Text.
4050 A CMerge with target Text and other CExpr.
4051 A CIncrease with target Text and amount CExpr.
4052 A CDecrease with target Text and amount CExpr.
4053 A CAppendToSeq with target Text and value CExpr.
4054 A CResolve with target Text.
4055 A CSync with target Text and channel CExpr.
4056 A CMount with target Text and path CExpr.
4057 A CConcurrent with branches Seq of Seq of CStmt.
4058 A CParallel with branches Seq of Seq of CStmt.
4059 A CLaunchTask with body Seq of CStmt and handle Text.
4060 A CStopTask with handle CExpr.
4061 A CSelect with branches Seq of CSelectBranch.
4062 A CCreatePipe with name Text and capacity CExpr.
4063 A CSendPipe with chan Text and value CExpr.
4064 A CReceivePipe with chan Text and target Text.
4065 A CTrySendPipe with chan Text and value CExpr.
4066 A CTryReceivePipe with chan Text and target Text.
4067 A CSpawn with agentType Text and target Text.
4068 A CSendMessage with target CExpr and msg CExpr.
4069 A CAwaitMessage with target Text.
4070 A CListen with addr CExpr and handler Text.
4071 A CConnectTo with addr CExpr and target Text.
4072 A CZone with name Text and kind Text and body Seq of CStmt.
4073
4074## A CSelectBranch is one of:
4075 A CSelectRecv with chan Text and var Text and body Seq of CStmt.
4076 A CSelectTimeout with duration CExpr and body Seq of CStmt.
4077
4078## A CMatchArm is one of:
4079 A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
4080 A COtherwise with body Seq of CStmt.
4081
4082## A CFunc is one of:
4083 A CFuncDef with name Text and params Seq of Text and paramTypes Seq of Text and returnType Text and body Seq of CStmt.
4084
4085## A CProgram is one of:
4086 A CProg with funcs Seq of CFunc and main Seq of CStmt.
4087
4088## A PEState is one of:
4089 A PEStateR with env Map of Text to CVal and funcs Map of Text to CFunc and depth Int and staticEnv Map of Text to CExpr and specResults Map of Text to CExpr and onStack Seq of Text.
4090
4091## A CVal is one of:
4092 A VInt with value Int.
4093 A VFloat with value Real.
4094 A VBool with value Bool.
4095 A VText with value Text.
4096 A VSeq with items Seq of CVal.
4097 A VMap with entries Map of Text to CVal.
4098 A VError with msg Text.
4099 A VNothing.
4100 A VSet with items Seq of CVal.
4101 A VOption with inner CVal and present Bool.
4102 A VTuple with items Seq of CVal.
4103 A VStruct with typeName Text and fields Map of Text to CVal.
4104 A VVariant with typeName Text and variantName Text and fields Seq of CVal.
4105 A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
4106 A VDuration with millis Int.
4107 A VDate with year Int and month Int and day Int.
4108 A VMoment with millis Int.
4109 A VSpan with startMillis Int and endMillis Int.
4110 A VTime with hour Int and minute Int and second Int.
4111 A VCrdt with kind Text and state Map of Text to CVal.
4112"#;
4113
4114pub fn quote_pe_source() -> Result<String, String> {
4122 let pe_source = pe_source_text();
4123 let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
4124 let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
4125 Ok(format!("{}\n{}", pe_source, encoded))
4126}
4127
4128pub fn projection2_source() -> Result<String, String> {
4142 let pe_source = pe_source_text();
4143
4144 let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
4145
4146 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
4147}
4148
4149pub fn projection3_source() -> Result<String, String> {
4164 let pe_source = pe_source_text();
4165
4166 let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
4167
4168 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
4169}
4170
4171pub fn run_logos_source(source: &str) -> Result<String, String> {
4176 let compile_output = compile_program_full(source)
4177 .map_err(|e| format!("Compilation failed: {:?}", e))?;
4178
4179 let temp_base = std::env::temp_dir().join("logos_run_source");
4181 std::fs::create_dir_all(&temp_base)
4182 .map_err(|e| format!("mkdir failed: {}", e))?;
4183
4184 let pkg_name = format!(
4185 "logos_run_{}_{}",
4186 std::process::id(),
4187 std::time::SystemTime::now()
4188 .duration_since(std::time::UNIX_EPOCH)
4189 .unwrap()
4190 .as_nanos()
4191 );
4192 let project_dir = temp_base.join(&pkg_name);
4193
4194 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
4196 let workspace_root = manifest_dir.parent().unwrap().parent().unwrap();
4197
4198 let cargo_toml = format!(
4199 r#"[package]
4200name = "{}"
4201version = "0.1.0"
4202edition = "2021"
4203
4204[dependencies]
4205logicaffeine-data = {{ path = "{}/crates/logicaffeine_data" }}
4206logicaffeine-system = {{ path = "{}/crates/logicaffeine_system", features = ["full"] }}
4207tokio = {{ version = "1", features = ["rt-multi-thread", "macros"] }}
4208serde = {{ version = "1", features = ["derive"] }}
4209rayon = "1"
4210"#,
4211 pkg_name,
4212 workspace_root.display(),
4213 workspace_root.display(),
4214 );
4215
4216 std::fs::create_dir_all(project_dir.join("src"))
4217 .map_err(|e| format!("mkdir failed: {}", e))?;
4218 std::fs::write(project_dir.join("Cargo.toml"), cargo_toml)
4219 .map_err(|e| format!("Write Cargo.toml failed: {}", e))?;
4220 std::fs::write(project_dir.join("src/main.rs"), &compile_output.rust_code)
4221 .map_err(|e| format!("Write main.rs failed: {}", e))?;
4222
4223 let target_dir = std::env::temp_dir().join("logos_e2e_cache");
4225 std::fs::create_dir_all(&target_dir)
4226 .map_err(|e| format!("mkdir target failed: {}", e))?;
4227
4228 let output = std::process::Command::new("cargo")
4229 .args(["run", "--quiet"])
4230 .current_dir(&project_dir)
4231 .env("CARGO_TARGET_DIR", &target_dir)
4232 .env("RUST_MIN_STACK", "268435456")
4233 .output()
4234 .map_err(|e| format!("cargo run failed: {}", e))?;
4235
4236 let _ = std::fs::remove_dir_all(&project_dir);
4238
4239 if !output.status.success() {
4240 return Err(format!(
4241 "Execution failed:\nstderr: {}\nstdout: {}",
4242 String::from_utf8_lossy(&output.stderr),
4243 String::from_utf8_lossy(&output.stdout),
4244 ));
4245 }
4246
4247 Ok(String::from_utf8_lossy(&output.stdout).to_string())
4248}
4249
4250pub struct GenuineProjectionResult {
4253 pub source: String,
4256 pub block_entry: String,
4259 pub expr_entry: Option<String>,
4261}
4262
4263fn discover_entry_points(residual: &str, block_prefix: &str, expr_prefix: &str)
4266 -> (String, Option<String>)
4267{
4268 let mut block_entry = String::new();
4269 let mut expr_entry = None;
4270 for line in residual.lines() {
4271 let trimmed = line.trim();
4272 if let Some(rest) = trimmed.strip_prefix("## To ") {
4273 let name = rest.split(" (").next().unwrap_or("").trim();
4275 if name.starts_with(block_prefix) && block_entry.is_empty() {
4276 block_entry = name.to_string();
4277 } else if name.starts_with(expr_prefix) && expr_entry.is_none() {
4278 expr_entry = Some(name.to_string());
4279 }
4280 }
4281 }
4282 (block_entry, expr_entry)
4283}
4284
4285pub fn projection1_source_real(core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
4290 let full_source = if program.contains("## Main") || program.contains("## To ") {
4291 program.to_string()
4292 } else {
4293 format!("## Main\n{}", program)
4294 };
4295
4296 let encoded = encode_program_source(&full_source)
4298 .map_err(|e| format!("Failed to encode program: {:?}", e))?;
4299
4300 let pe_source = pe_source_text();
4302 let decompile_source = decompile_source_text();
4303
4304 let actual_core_types = if core_types.is_empty() { CORE_TYPES_FOR_PE } else { core_types };
4306
4307 let driver = r#"
4308 Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4309 Let residual be peBlock(encodedMain, state).
4310 Let source be decompileBlock(residual, 0).
4311 Show source.
4312"#;
4313
4314 let combined = format!(
4315 "{}\n{}\n{}\n## Main\n{}\n{}",
4316 actual_core_types,
4317 pe_source,
4318 decompile_source,
4319 encoded,
4320 driver,
4321 );
4322
4323 let raw_residual = run_logos_source(&combined)?;
4325 let trimmed = raw_residual.trim();
4326
4327 if trimmed.is_empty() {
4329 return Ok("## Main\n".to_string());
4330 }
4331
4332 if trimmed.contains("## To ") {
4334 Ok(trimmed.to_string())
4335 } else {
4336 Ok(format!("## Main\n{}", trimmed))
4337 }
4338}
4339
4340pub fn run_genuine_p2_on_target(program: &str, core_types: &str, interpreter: &str) -> Result<String, String> {
4352 let pe_mini = pe_mini_source_text();
4353 let pe = pe_source_text();
4354
4355 let full_source = if program.contains("## Main") || program.contains("## To ") {
4356 program.to_string()
4357 } else {
4358 format!("## Main\n{}", program)
4359 };
4360
4361 let target_encoded = encode_program_source(&full_source)
4366 .map_err(|e| format!("Failed to encode target: {:?}", e))?;
4367 let pe_mini_prog = format!(
4368 "{}\n{}\n{}\n## Main\n{}\n\
4369 Let compileEnv be a new Map of Text to CVal.\n\
4370 Let compileState be makePeState(compileEnv, encodedFuncMap, 200).\n\
4371 Let compiled be peBlockM(encodedMain, compileState).\n\
4372 Let runEnv be a new Map of Text to CVal.\n\
4373 coreExecBlock(compiled, runEnv, encodedFuncMap).\n",
4374 core_types, pe_mini, interpreter, target_encoded
4375 );
4376
4377 let encoded = encode_program_source_compact(&pe_mini_prog)
4379 .map_err(|e| format!("Failed to encode pe_mini+target for P2: {:?}", e))?;
4380
4381 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 500).
4386 Let residual be peBlock(encodedMain, state).
4387 Let allFuncs be peFuncs(state).
4388 Let runEnv be a new Map of Text to CVal.
4389 coreExecBlock(residual, runEnv, allFuncs).
4390"#;
4391 let combined = format!(
4392 "{}\n{}\n{}\n## Main\n{}\n{}",
4393 CORE_TYPES_FOR_PE, pe, interpreter, encoded, driver
4394 );
4395
4396 run_logos_source(&combined)
4397}
4398
4399pub fn run_genuine_p3_on_target(program: &str, core_types: &str, interpreter: &str) -> Result<String, String> {
4401 let pe_bti = pe_bti_source_text();
4402 let pe = pe_source_text();
4403
4404 let full_source = if program.contains("## Main") || program.contains("## To ") {
4405 program.to_string()
4406 } else {
4407 format!("## Main\n{}", program)
4408 };
4409
4410 let bti_types = CORE_TYPES_FOR_PE
4411 .replace("specResults", "memoCache")
4412 .replace("onStack", "callGuard");
4413
4414 let target_encoded = encode_program_source(&full_source)
4415 .map_err(|e| format!("Failed to encode target: {:?}", e))?;
4416 let pe_bti_prog = format!(
4417 "{}\n{}\n{}\n## Main\n{}\n\
4418 Let compileEnv be a new Map of Text to CVal.\n\
4419 Let compileState be makePeState(compileEnv, encodedFuncMap, 200).\n\
4420 Let compiled be peBlockB(encodedMain, compileState).\n\
4421 Let runEnv be a new Map of Text to CVal.\n\
4422 coreExecBlock(compiled, runEnv, encodedFuncMap).\n",
4423 bti_types, pe_bti, interpreter, target_encoded
4424 );
4425
4426 let encoded = encode_program_source_compact(&pe_bti_prog)
4427 .map_err(|e| format!("Failed to encode pe_bti+target for P3: {:?}", e))?;
4428
4429 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4431 Let residual be peBlock(encodedMain, state).
4432 Let runEnv be a new Map of Text to CVal.
4433 coreExecBlock(residual, runEnv, encodedFuncMap).
4434"#;
4435 let combined = format!(
4436 "{}\n{}\n{}\n## Main\n{}\n{}",
4437 CORE_TYPES_FOR_PE, pe, interpreter, encoded, driver
4438 );
4439
4440 run_logos_source(&combined)
4441}
4442
4443pub fn projection2_source_real(_core_types: &str, _interpreter: &str) -> Result<GenuineProjectionResult, String> {
4453 let pe_mini = pe_mini_source_text();
4454 let pe = pe_source_text();
4455 let decompile = decompile_source_text();
4456
4457 let program = format!(
4467 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockM(targetStmts, state).\n Show \"done\".\n",
4468 CORE_TYPES_FOR_PE, pe_mini
4469 );
4470
4471 let encoded = encode_program_source_compact(&program)
4473 .map_err(|e| format!("Failed to encode pe_mini for P2: {:?}", e))?;
4474
4475 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4479 Let residual be peBlock(encodedMain, state).
4480 Let nl be chr(10).
4481 Let mutable output be "".
4482 Let specFuncs be peFuncs(state).
4483 Let mutable allNames be collectCallNames(residual).
4484 Let mutable emitted be a new Map of Text to Bool.
4485 Let mutable changed be true.
4486 While changed:
4487 Set changed to false.
4488 Let mutable toAdd be a new Seq of Text.
4489 Repeat for fnKey in allNames:
4490 Let fkStr be "{fnKey}".
4491 If emitted contains fkStr:
4492 Let skipE be true.
4493 Otherwise:
4494 Set item fkStr of emitted to true.
4495 Let fkStr2 be "{fnKey}".
4496 If specFuncs contains fkStr2:
4497 Let fdef be item fkStr2 of specFuncs.
4498 Inspect fdef:
4499 When CFuncDef (fn0, ps0, pt0, rt0, body0):
4500 Let children be collectCallNames(body0).
4501 Repeat for child in children:
4502 Let childStr be "{child}".
4503 If not emitted contains childStr:
4504 Push child to toAdd.
4505 Set changed to true.
4506 Otherwise:
4507 Let skipF be true.
4508 Repeat for ta in toAdd:
4509 Push ta to allNames.
4510 Repeat for fnKey in allNames:
4511 Let fkStr be "{fnKey}".
4512 If specFuncs contains fkStr:
4513 Let fdef be item fkStr of specFuncs.
4514 Let funcSrc be decompileFunc(fdef).
4515 If the length of funcSrc is greater than 0:
4516 Set output to "{output}{funcSrc}{nl}".
4517 Let mainSrc be decompileBlock(residual, 0).
4518 Set output to "{output}## Main{nl}{mainSrc}".
4519 Show output.
4520"#;
4521 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4522
4523 let result = run_logos_source(&combined)?;
4524
4525 let result = fix_decompiled_types(&result, &[
4530 ("peExprM_", "(e: CExpr) -> CExpr:"),
4531 ("peBlockM_", "(stmts: Seq of CStmt) -> Seq of CStmt:"),
4532 ("checkLiteralM_", "(e: CExpr) -> Bool:"),
4533 ("exprToValM_", "(e: CExpr) -> CVal:"),
4534 ("valToExprM_", "(v: CVal) -> CExpr:"),
4535 ("evalBinOpM_", "(binOp: Text) and (lv: CVal) and (rv: CVal) -> CVal:"),
4536 ("isCopyPropSafeM_", "(e: CExpr) -> Bool:"),
4537 ("checkVNothingM_", "(v: CVal) -> Bool:"),
4538 ("hasReturnM_", "(stmts: Seq of CStmt) -> Bool:"),
4539 ("extractReturnM_", "(stmts: Seq of CStmt) -> CExpr:"),
4540 ("validateExtractReturnM_", "(result: CExpr) and (bodyStmts: Seq of CStmt) -> CExpr:"),
4541 ("makeKeyM_", "(fnName: Text) and (args: Seq of CExpr) -> Text:"),
4542 ("exprToKeyPartM_", "(e: CExpr) -> Text:"),
4543 ("collectSetVarsM_", "(stmts: Seq of CStmt) -> Seq of Text:"),
4544 ("peEnvM_", "(st: PEMiniState) -> Map of Text to CVal:"),
4545 ("peFuncsM_", "(st: PEMiniState) -> Map of Text to CFunc:"),
4546 ("peDepthM_", "(st: PEMiniState) -> Int:"),
4547 ("peStaticEnvM_", "(st: PEMiniState) -> Map of Text to CExpr:"),
4548 ("peMemoCacheM_", "(st: PEMiniState) -> Map of Text to CExpr:"),
4549 ("peStateWithEnvDepthM_", "(st: PEMiniState) and (newEnv: Map of Text to CVal) and (d: Int) -> PEMiniState:"),
4550 ("peStateWithEnvDepthStaticM_", "(st: PEMiniState) and (newEnv: Map of Text to CVal) and (d: Int) and (newSe: Map of Text to CExpr) -> PEMiniState:"),
4551 ]);
4552
4553 let (block_entry, expr_entry) = discover_entry_points(&result, "peBlockM_", "peExprM_");
4555 if block_entry.is_empty() {
4556 return Err("Genuine P2: no peBlockM_ entry found in residual".to_string());
4557 }
4558
4559 let func_defs_only = strip_main_block(&result);
4562
4563 let pe_mini_helpers = pe_mini_source_text();
4567
4568 let alias = format!(
4572 "\n## To compileBlock (stmts: Seq of CStmt) -> Seq of CStmt:\n Return {}(stmts).\n",
4573 block_entry
4574 );
4575
4576 let combined = format!("{}\n{}\n{}", pe_mini_helpers, func_defs_only, alias);
4580 let full_source = deduplicate_functions(&combined);
4581
4582 Ok(GenuineProjectionResult {
4583 source: full_source,
4584 block_entry: "compileBlock".to_string(),
4585 expr_entry,
4586 })
4587}
4588
4589pub fn genuine_projection2_residual() -> Result<String, String> {
4600 let pe_mini = pe_mini_source_text();
4601 let pe = pe_source_text();
4602 let decompile = decompile_source_text();
4603
4604 let program = format!(
4606 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockM(targetStmts, state).\n Show \"done\".\n",
4607 CORE_TYPES_FOR_PE, pe_mini
4608 );
4609
4610 let encoded = encode_program_source_compact(&program)
4612 .map_err(|e| format!("Failed to encode pe_mini: {:?}", e))?;
4613
4614 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4616 Let residual be peBlock(encodedMain, state).
4617 Let nl be chr(10).
4618 Let mutable output be "".
4619 Let specFuncs be peFuncs(state).
4620 Let specNames be collectCallNames(residual).
4621 Repeat for sn in specNames:
4622 Let snKey be "{sn}".
4623 If specFuncs contains snKey:
4624 Let fdef be item snKey of specFuncs.
4625 Let funcSrc be decompileFunc(fdef).
4626 If the length of funcSrc is greater than 0:
4627 Set output to "{output}{funcSrc}{nl}".
4628 Let mainSrc be decompileBlock(residual, 0).
4629 Set output to "{output}## Main{nl}{mainSrc}".
4630 Show output.
4631"#;
4632 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4633
4634 let result = run_logos_source(&combined)?;
4635 Ok(result)
4636}
4637
4638pub fn genuine_projection3_residual() -> Result<String, String> {
4649 let pe_bti = pe_bti_source_text();
4650 let pe = pe_source_text();
4651 let decompile = decompile_source_text();
4652
4653 let bti_types = CORE_TYPES_FOR_PE
4655 .replace("specResults", "memoCache")
4656 .replace("onStack", "callGuard");
4657
4658 let program = format!(
4660 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockB(targetStmts, state).\n Show \"done\".\n",
4661 bti_types, pe_bti
4662 );
4663
4664 let encoded = encode_program_source_compact(&program)
4666 .map_err(|e| format!("Failed to encode pe_bti: {:?}", e))?;
4667
4668 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4670 Let residual be peBlock(encodedMain, state).
4671 Let nl be chr(10).
4672 Let mutable output be "".
4673 Let specFuncs be peFuncs(state).
4674 Let specNames be collectCallNames(residual).
4675 Repeat for sn in specNames:
4676 Let snKey be "{sn}".
4677 If specFuncs contains snKey:
4678 Let fdef be item snKey of specFuncs.
4679 Let funcSrc be decompileFunc(fdef).
4680 If the length of funcSrc is greater than 0:
4681 Set output to "{output}{funcSrc}{nl}".
4682 Let mainSrc be decompileBlock(residual, 0).
4683 Set output to "{output}## Main{nl}{mainSrc}".
4684 Show output.
4685"#;
4686 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4687
4688 let result = run_logos_source(&combined)?;
4689 Ok(result)
4690}
4691
4692pub fn projection3_source_real(_core_types: &str) -> Result<GenuineProjectionResult, String> {
4701 let pe_bti = pe_bti_source_text();
4702 let pe = pe_source_text();
4703 let decompile = decompile_source_text();
4704
4705 let bti_types = CORE_TYPES_FOR_PE
4707 .replace("specResults", "memoCache")
4708 .replace("onStack", "callGuard");
4709
4710 let program = format!(
4712 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockB(targetStmts, state).\n Show \"done\".\n",
4713 bti_types, pe_bti
4714 );
4715
4716 let encoded = encode_program_source_compact(&program)
4718 .map_err(|e| format!("Failed to encode pe_bti for P3: {:?}", e))?;
4719
4720 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4722 Let residual be peBlock(encodedMain, state).
4723 Let nl be chr(10).
4724 Let mutable output be "".
4725 Let specFuncs be peFuncs(state).
4726 Let mutable allNames be collectCallNames(residual).
4727 Let mutable emitted be a new Map of Text to Bool.
4728 Let mutable changed be true.
4729 While changed:
4730 Set changed to false.
4731 Let mutable toAdd be a new Seq of Text.
4732 Repeat for fnKey in allNames:
4733 Let fkStr be "{fnKey}".
4734 If emitted contains fkStr:
4735 Let skipE be true.
4736 Otherwise:
4737 Set item fkStr of emitted to true.
4738 Let fkStr2 be "{fnKey}".
4739 If specFuncs contains fkStr2:
4740 Let fdef be item fkStr2 of specFuncs.
4741 Inspect fdef:
4742 When CFuncDef (fn0, ps0, pt0, rt0, body0):
4743 Let children be collectCallNames(body0).
4744 Repeat for child in children:
4745 Let childStr be "{child}".
4746 If not emitted contains childStr:
4747 Push child to toAdd.
4748 Set changed to true.
4749 Otherwise:
4750 Let skipF be true.
4751 Repeat for ta in toAdd:
4752 Push ta to allNames.
4753 Repeat for fnKey in allNames:
4754 Let fkStr be "{fnKey}".
4755 If specFuncs contains fkStr:
4756 Let fdef be item fkStr of specFuncs.
4757 Let funcSrc be decompileFunc(fdef).
4758 If the length of funcSrc is greater than 0:
4759 Set output to "{output}{funcSrc}{nl}".
4760 Let mainSrc be decompileBlock(residual, 0).
4761 Set output to "{output}## Main{nl}{mainSrc}".
4762 Show output.
4763"#;
4764 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4765
4766 let result = run_logos_source(&combined)?;
4767
4768 let result = fix_decompiled_types(&result, &[
4771 ("peExprB_", "(e: CExpr) -> CExpr:"),
4772 ("peBlockB_", "(stmts: Seq of CStmt) -> Seq of CStmt:"),
4773 ("isStatic_", "(e: CExpr) -> Bool:"),
4774 ("isLiteral_", "(e: CExpr) -> Bool:"),
4775 ("allStatic_", "(args: Seq of CExpr) -> Bool:"),
4776 ("exprToVal_", "(e: CExpr) -> CVal:"),
4777 ("valToExpr_", "(v: CVal) -> CExpr:"),
4778 ("evalBinOp_", "(binOp: Text) and (lv: CVal) and (rv: CVal) -> CVal:"),
4779 ("isCopyPropSafe_", "(e: CExpr) -> Bool:"),
4780 ("isVNothing_", "(v: CVal) -> Bool:"),
4781 ("hasReturn_", "(stmts: Seq of CStmt) -> Bool:"),
4782 ("extractReturnB_", "(stmts: Seq of CStmt) -> CExpr:"),
4783 ("makeKey_", "(fnName: Text) and (args: Seq of CExpr) -> Text:"),
4784 ("exprToKeyPartB_", "(e: CExpr) -> Text:"),
4785 ("collectSetVars_", "(stmts: Seq of CStmt) -> Seq of Text:"),
4786 ]);
4787
4788 let (block_entry, expr_entry) = discover_entry_points(&result, "peBlockB_", "peExprB_");
4790 if block_entry.is_empty() {
4791 return Err("Genuine P3: no peBlockB_ entry found in residual".to_string());
4792 }
4793
4794 let func_defs_only = strip_main_block(&result);
4796
4797 let pe_bti_helpers = pe_bti_source_text();
4799
4800 let alias = format!(
4803 "\n## To cogenBlock (stmts: Seq of CStmt) -> Seq of CStmt:\n Return {}(stmts).\n",
4804 block_entry
4805 );
4806 let combined = format!("{}\n{}\n{}", pe_bti_helpers, func_defs_only, alias);
4807 let full_source = deduplicate_functions(&combined);
4808
4809 Ok(GenuineProjectionResult {
4810 source: full_source,
4811 block_entry: "cogenBlock".to_string(),
4812 expr_entry,
4813 })
4814}
4815
4816fn deduplicate_functions(source: &str) -> String {
4818 let mut seen = std::collections::HashSet::new();
4819 let mut result = String::with_capacity(source.len());
4820 let mut skip_until_next = false;
4821 for line in source.lines() {
4822 let trimmed = line.trim();
4823 if let Some(rest) = trimmed.strip_prefix("## To ") {
4824 let name = rest.split(' ').next().unwrap_or("");
4825 if !seen.insert(name.to_string()) {
4826 skip_until_next = true;
4827 continue;
4828 }
4829 skip_until_next = false;
4830 } else if trimmed.starts_with("## Main") {
4831 skip_until_next = false;
4832 } else if skip_until_next {
4833 if !trimmed.starts_with("## ") {
4835 continue;
4836 }
4837 skip_until_next = false;
4838 }
4839 result.push_str(line);
4840 result.push('\n');
4841 }
4842 result
4843}
4844
4845fn strip_main_block(source: &str) -> String {
4849 let mut result = String::with_capacity(source.len());
4850 let mut in_main = false;
4851 for line in source.lines() {
4852 let trimmed = line.trim();
4853 if trimmed == "## Main" {
4854 in_main = true;
4855 continue;
4856 }
4857 if in_main {
4858 if trimmed.starts_with("## To ") {
4860 in_main = false;
4861 } else {
4862 continue;
4863 }
4864 }
4865 result.push_str(line);
4866 result.push('\n');
4867 }
4868 result
4869}
4870
4871fn extract_main_block(source: &str) -> String {
4873 let mut result = String::new();
4874 let mut in_main = false;
4875 for line in source.lines() {
4876 let trimmed = line.trim();
4877 if trimmed == "## Main" {
4878 in_main = true;
4879 continue;
4880 }
4881 if in_main {
4882 if trimmed.starts_with("## To ") {
4883 break;
4884 }
4885 result.push_str(line);
4886 result.push('\n');
4887 }
4888 }
4889 result
4890}
4891
4892fn fix_decompiled_types(source: &str, type_map: &[(&str, &str)]) -> String {
4896 let mut result = String::with_capacity(source.len());
4897 for line in source.lines() {
4898 let trimmed = line.trim();
4899 if let Some(rest) = trimmed.strip_prefix("## To ") {
4900 let name = rest.split(' ').next().unwrap_or("");
4901 let mut fixed = false;
4902 for (prefix, sig) in type_map {
4903 if name.starts_with(prefix) {
4904 result.push_str(&format!("## To {} {}\n", name, sig));
4905 fixed = true;
4906 break;
4907 }
4908 }
4909 if !fixed {
4910 result.push_str(line);
4911 result.push('\n');
4912 }
4913 } else {
4914 result.push_str(line);
4915 result.push('\n');
4916 }
4917 }
4918 let result = result
4919 .replace("Seq of Any", "Seq of CExpr")
4920 .replace("Set of Any", "Set of CExpr")
4921 .replace(": Any)", ": CExpr)")
4922 .replace("-> Any:", "-> CExpr:");
4923 result
4924}
4925
4926fn replace_word(source: &str, from: &str, to: &str) -> String {
4927 let mut result = String::with_capacity(source.len());
4928 let mut remaining = source;
4929 while let Some(pos) = remaining.find(from) {
4930 let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
4931 let after_pos = pos + from.len();
4932 let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
4933 let is_word = !before.is_ascii_alphanumeric() && before != b'_'
4934 && !after.is_ascii_alphanumeric() && after != b'_';
4935 result.push_str(&remaining[..pos]);
4936 if is_word {
4937 result.push_str(to);
4938 } else {
4939 result.push_str(from);
4940 }
4941 remaining = &remaining[after_pos..];
4942 }
4943 result.push_str(remaining);
4944 result
4945}
4946
4947#[cfg(test)]
4948mod tests {
4949 use super::*;
4950
4951 #[test]
4952 fn test_compile_let_statement() {
4953 let source = "## Main\nLet x be 5.";
4954 let result = compile_to_rust(source);
4955 assert!(result.is_ok(), "Should compile: {:?}", result);
4956 let rust = result.unwrap();
4957 assert!(rust.contains("fn main()"));
4958 assert!(rust.contains("let x = 5;"));
4959 }
4960
4961 #[test]
4962 fn test_compile_return_statement() {
4963 let source = "## Main\nReturn 42.";
4964 let result = compile_to_rust(source);
4965 assert!(result.is_ok(), "Should compile: {:?}", result);
4966 let rust = result.unwrap();
4967 assert!(rust.contains("return 42;"));
4968 }
4969
4970}