1use std::collections::{HashMap, HashSet};
74use std::fs;
75use std::io::Write;
76use std::path::Path;
77use std::process::Command;
78
79const CRATES_DATA_PATH: &str = "crates/logicaffeine_data";
81const CRATES_SYSTEM_PATH: &str = "crates/logicaffeine_system";
82
83use std::fmt::Write as FmtWrite;
84
85use crate::analysis::{DiscoveryPass, EscapeChecker, OwnershipChecker, PolicyRegistry};
86use crate::arena::Arena;
87use crate::arena_ctx::AstContext;
88use crate::ast::{Expr, MatchArm, Stmt, TypeExpr};
89use crate::ast::stmt::{BinaryOpKind, ClosureBody, Literal, Pattern, ReadSource, SelectBranch, StringPart};
90use crate::codegen::{codegen_program, generate_c_header, generate_python_bindings, generate_typescript_bindings};
91use crate::diagnostic::{parse_rustc_json, translate_diagnostics, LogosError};
92use crate::drs::WorldState;
93use crate::error::ParseError;
94use crate::intern::Interner;
95use crate::lexer::Lexer;
96use crate::parser::Parser;
97use crate::sourcemap::SourceMap;
98
99#[derive(Debug, Clone)]
101pub struct CrateDependency {
102 pub name: String,
103 pub version: String,
104 pub features: Vec<String>,
105}
106
107#[derive(Debug)]
109pub struct CompileOutput {
110 pub rust_code: String,
111 pub dependencies: Vec<CrateDependency>,
112 pub c_header: Option<String>,
114 pub python_bindings: Option<String>,
116 pub typescript_types: Option<String>,
118 pub typescript_bindings: Option<String>,
120}
121
122pub fn interpret_program(source: &str) -> Result<String, ParseError> {
140 let result = crate::ui_bridge::interpret_for_ui_sync(source);
141 if let Some(err) = result.error {
142 Err(ParseError {
143 kind: crate::error::ParseErrorKind::Custom(err),
144 span: crate::token::Span::default(),
145 })
146 } else {
147 Ok(result.lines.join("\n"))
148 }
149}
150
151pub fn compile_to_rust(source: &str) -> Result<String, ParseError> {
184 compile_program_full(source).map(|o| o.rust_code)
185}
186
187pub fn compile_to_c(source: &str) -> Result<String, ParseError> {
192 let mut interner = Interner::new();
193 let mut lexer = Lexer::new(source, &mut interner);
194 let tokens = lexer.tokenize();
195
196 let (type_registry, _policy_registry) = {
197 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
198 let result = discovery.run_full();
199 (result.types, result.policies)
200 };
201 let codegen_registry = type_registry.clone();
202
203 let mut world_state = WorldState::new();
204 let expr_arena = Arena::new();
205 let term_arena = Arena::new();
206 let np_arena = Arena::new();
207 let sym_arena = Arena::new();
208 let role_arena = Arena::new();
209 let pp_arena = Arena::new();
210 let stmt_arena: Arena<Stmt> = Arena::new();
211 let imperative_expr_arena: Arena<Expr> = Arena::new();
212 let type_expr_arena: Arena<TypeExpr> = Arena::new();
213
214 let ast_ctx = AstContext::with_types(
215 &expr_arena, &term_arena, &np_arena, &sym_arena,
216 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
217 &type_expr_arena,
218 );
219
220 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
221 let stmts = parser.parse_program()?;
222 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
223
224 Ok(crate::codegen_c::codegen_program_c(&stmts, &codegen_registry, &interner))
225}
226
227pub fn compile_program_full(source: &str) -> Result<CompileOutput, ParseError> {
232 let mut interner = Interner::new();
233 let mut lexer = Lexer::new(source, &mut interner);
234 let tokens = lexer.tokenize();
235
236 let (type_registry, policy_registry) = {
238 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
239 let result = discovery.run_full();
240 (result.types, result.policies)
241 };
242 let codegen_registry = type_registry.clone();
244 let codegen_policies = policy_registry.clone();
245
246 let mut world_state = WorldState::new();
247 let expr_arena = Arena::new();
248 let term_arena = Arena::new();
249 let np_arena = Arena::new();
250 let sym_arena = Arena::new();
251 let role_arena = Arena::new();
252 let pp_arena = Arena::new();
253 let stmt_arena: Arena<Stmt> = Arena::new();
254 let imperative_expr_arena: Arena<Expr> = Arena::new();
255 let type_expr_arena: Arena<TypeExpr> = Arena::new();
256
257 let ast_ctx = AstContext::with_types(
258 &expr_arena,
259 &term_arena,
260 &np_arena,
261 &sym_arena,
262 &role_arena,
263 &pp_arena,
264 &stmt_arena,
265 &imperative_expr_arena,
266 &type_expr_arena,
267 );
268
269 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
271 let stmts = parser.parse_program()?;
274
275 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
277
278 let mut dependencies = extract_dependencies(&stmts, &interner)?;
280
281 let needs_wasm_bindgen = stmts.iter().any(|stmt| {
283 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
284 interner.resolve(*target).eq_ignore_ascii_case("wasm")
285 } else {
286 false
287 }
288 });
289 if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
290 dependencies.push(CrateDependency {
291 name: "wasm-bindgen".to_string(),
292 version: "0.2".to_string(),
293 features: vec![],
294 });
295 }
296
297 let mut escape_checker = EscapeChecker::new(&interner);
300 escape_checker.check_program(&stmts).map_err(|e| {
301 ParseError {
304 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
305 span: e.span,
306 }
307 })?;
308
309 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
313 .map_err(|e| ParseError {
314 kind: e.to_parse_error_kind(&interner),
315 span: crate::token::Span::default(),
316 })?;
317 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
318
319 let has_c = stmts.iter().any(|stmt| {
321 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
322 match export_target {
323 None => true,
324 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
325 }
326 } else {
327 false
328 }
329 });
330
331 let c_header = if has_c {
332 Some(generate_c_header(&stmts, "module", &interner, &codegen_registry))
333 } else {
334 None
335 };
336
337 if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
339 dependencies.push(CrateDependency {
340 name: "serde_json".to_string(),
341 version: "1".to_string(),
342 features: vec![],
343 });
344 }
345
346 let python_bindings = if has_c {
347 Some(generate_python_bindings(&stmts, "module", &interner, &codegen_registry))
348 } else {
349 None
350 };
351
352 let (typescript_bindings, typescript_types) = if has_c {
353 let (js, dts) = generate_typescript_bindings(&stmts, "module", &interner, &codegen_registry);
354 (Some(js), Some(dts))
355 } else {
356 (None, None)
357 };
358
359 Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
360}
361
362fn extract_dependencies(stmts: &[Stmt], interner: &Interner) -> Result<Vec<CrateDependency>, ParseError> {
368 use std::collections::HashMap;
369
370 let mut seen: HashMap<String, String> = HashMap::new(); let mut deps: Vec<CrateDependency> = Vec::new();
372
373 for stmt in stmts {
374 if let Stmt::Require { crate_name, version, features, span } = stmt {
375 let name = interner.resolve(*crate_name).to_string();
376 let ver = interner.resolve(*version).to_string();
377
378 if let Some(existing_ver) = seen.get(&name) {
379 if *existing_ver != ver {
380 return Err(ParseError {
381 kind: crate::error::ParseErrorKind::Custom(format!(
382 "Conflicting versions for crate \"{}\": \"{}\" and \"{}\".",
383 name, existing_ver, ver
384 )),
385 span: *span,
386 });
387 }
388 } else {
390 seen.insert(name.clone(), ver.clone());
391 deps.push(CrateDependency {
392 name,
393 version: ver,
394 features: features.iter().map(|f| interner.resolve(*f).to_string()).collect(),
395 });
396 }
397 }
398 }
399
400 Ok(deps)
401}
402
403pub fn compile_to_rust_checked(source: &str) -> Result<String, ParseError> {
439 let mut interner = Interner::new();
440 let mut lexer = Lexer::new(source, &mut interner);
441 let tokens = lexer.tokenize();
442
443 let (type_registry, policy_registry) = {
445 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
446 let result = discovery.run_full();
447 (result.types, result.policies)
448 };
449 let codegen_registry = type_registry.clone();
451 let codegen_policies = policy_registry.clone();
452
453 let mut world_state = WorldState::new();
454 let expr_arena = Arena::new();
455 let term_arena = Arena::new();
456 let np_arena = Arena::new();
457 let sym_arena = Arena::new();
458 let role_arena = Arena::new();
459 let pp_arena = Arena::new();
460 let stmt_arena: Arena<Stmt> = Arena::new();
461 let imperative_expr_arena: Arena<Expr> = Arena::new();
462 let type_expr_arena: Arena<TypeExpr> = Arena::new();
463
464 let ast_ctx = AstContext::with_types(
465 &expr_arena,
466 &term_arena,
467 &np_arena,
468 &sym_arena,
469 &role_arena,
470 &pp_arena,
471 &stmt_arena,
472 &imperative_expr_arena,
473 &type_expr_arena,
474 );
475
476 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
478 let stmts = parser.parse_program()?;
479
480 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
482
483 let mut escape_checker = EscapeChecker::new(&interner);
485 escape_checker.check_program(&stmts).map_err(|e| {
486 ParseError {
487 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
488 span: e.span,
489 }
490 })?;
491
492 let mut ownership_checker = OwnershipChecker::new(&interner);
495 ownership_checker.check_program(&stmts).map_err(|e| {
496 ParseError {
497 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
498 span: e.span,
499 }
500 })?;
501
502 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
503 .map_err(|e| ParseError {
504 kind: e.to_parse_error_kind(&interner),
505 span: crate::token::Span::default(),
506 })?;
507 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
508
509 Ok(rust_code)
510}
511
512#[cfg(feature = "verification")]
559pub fn compile_to_rust_verified(source: &str) -> Result<String, ParseError> {
560 use crate::verification::VerificationPass;
561
562 let mut interner = Interner::new();
563 let mut lexer = Lexer::new(source, &mut interner);
564 let tokens = lexer.tokenize();
565
566 let (type_registry, policy_registry) = {
568 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
569 let result = discovery.run_full();
570 (result.types, result.policies)
571 };
572 let codegen_registry = type_registry.clone();
574 let codegen_policies = policy_registry.clone();
575
576 let mut world_state = WorldState::new();
577 let expr_arena = Arena::new();
578 let term_arena = Arena::new();
579 let np_arena = Arena::new();
580 let sym_arena = Arena::new();
581 let role_arena = Arena::new();
582 let pp_arena = Arena::new();
583 let stmt_arena: Arena<Stmt> = Arena::new();
584 let imperative_expr_arena: Arena<Expr> = Arena::new();
585 let type_expr_arena: Arena<TypeExpr> = Arena::new();
586
587 let ast_ctx = AstContext::with_types(
588 &expr_arena,
589 &term_arena,
590 &np_arena,
591 &sym_arena,
592 &role_arena,
593 &pp_arena,
594 &stmt_arena,
595 &imperative_expr_arena,
596 &type_expr_arena,
597 );
598
599 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
601 let stmts = parser.parse_program()?;
602
603 let mut escape_checker = EscapeChecker::new(&interner);
605 escape_checker.check_program(&stmts).map_err(|e| {
606 ParseError {
607 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
608 span: e.span,
609 }
610 })?;
611
612 let mut verifier = VerificationPass::new(&interner);
614 verifier.verify_program(&stmts).map_err(|e| {
615 ParseError {
616 kind: crate::error::ParseErrorKind::Custom(format!(
617 "Verification Failed:\n\n{}",
618 e
619 )),
620 span: crate::token::Span::default(),
621 }
622 })?;
623
624 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
625 .map_err(|e| ParseError {
626 kind: e.to_parse_error_kind(&interner),
627 span: crate::token::Span::default(),
628 })?;
629 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
630
631 Ok(rust_code)
632}
633
634pub fn compile_to_dir(source: &str, output_dir: &Path) -> Result<(), CompileError> {
665 let output = compile_program_full(source).map_err(CompileError::Parse)?;
666
667 let src_dir = output_dir.join("src");
669 fs::create_dir_all(&src_dir).map_err(|e| CompileError::Io(e.to_string()))?;
670
671 let main_path = src_dir.join("main.rs");
673 let mut file = fs::File::create(&main_path).map_err(|e| CompileError::Io(e.to_string()))?;
674 file.write_all(output.rust_code.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
675
676 let mut cargo_toml = String::from(r#"[package]
678name = "logos_output"
679version = "0.1.0"
680edition = "2021"
681
682[dependencies]
683logicaffeine-data = { path = "./crates/logicaffeine_data" }
684logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full"] }
685tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
686
687[target.'cfg(target_os = "linux")'.dependencies]
688logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full", "io-uring"] }
689"#);
690
691 for dep in &output.dependencies {
693 if dep.features.is_empty() {
694 let _ = writeln!(cargo_toml, "{} = \"{}\"", dep.name, dep.version);
695 } else {
696 let feats = dep.features.iter()
697 .map(|f| format!("\"{}\"", f))
698 .collect::<Vec<_>>()
699 .join(", ");
700 let _ = writeln!(
701 cargo_toml,
702 "{} = {{ version = \"{}\", features = [{}] }}",
703 dep.name, dep.version, feats
704 );
705 }
706 }
707
708 cargo_toml.push_str("\n[profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\npanic = \"abort\"\nstrip = true\n");
709
710 let cargo_path = output_dir.join("Cargo.toml");
711 let mut file = fs::File::create(&cargo_path).map_err(|e| CompileError::Io(e.to_string()))?;
712 file.write_all(cargo_toml.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
713
714 let cargo_config_dir = output_dir.join(".cargo");
717 fs::create_dir_all(&cargo_config_dir).map_err(|e| CompileError::Io(e.to_string()))?;
718 let config_content = "[build]\nrustflags = [\"-C\", \"target-cpu=native\"]\n";
719 let config_path = cargo_config_dir.join("config.toml");
720 fs::write(&config_path, config_content).map_err(|e| CompileError::Io(e.to_string()))?;
721
722 copy_runtime_crates(output_dir)?;
724
725 Ok(())
726}
727
728pub fn copy_runtime_crates(output_dir: &Path) -> Result<(), CompileError> {
731 let crates_dir = output_dir.join("crates");
732 fs::create_dir_all(&crates_dir).map_err(|e| CompileError::Io(e.to_string()))?;
733
734 let workspace_root = find_workspace_root()?;
736
737 let data_src = workspace_root.join(CRATES_DATA_PATH);
739 let data_dest = crates_dir.join("logicaffeine_data");
740 copy_dir_recursive(&data_src, &data_dest)?;
741 deworkspace_cargo_toml(&data_dest.join("Cargo.toml"))?;
742
743 let system_src = workspace_root.join(CRATES_SYSTEM_PATH);
745 let system_dest = crates_dir.join("logicaffeine_system");
746 copy_dir_recursive(&system_src, &system_dest)?;
747 deworkspace_cargo_toml(&system_dest.join("Cargo.toml"))?;
748
749 let base_src = workspace_root.join("crates/logicaffeine_base");
751 let base_dest = crates_dir.join("logicaffeine_base");
752 copy_dir_recursive(&base_src, &base_dest)?;
753 deworkspace_cargo_toml(&base_dest.join("Cargo.toml"))?;
754
755 Ok(())
756}
757
758fn deworkspace_cargo_toml(cargo_toml_path: &Path) -> Result<(), CompileError> {
764 let content = fs::read_to_string(cargo_toml_path)
765 .map_err(|e| CompileError::Io(e.to_string()))?;
766
767 let mut result = String::with_capacity(content.len());
768 for line in content.lines() {
769 let trimmed = line.trim();
770 if trimmed == "edition.workspace = true" {
771 result.push_str("edition = \"2021\"");
772 } else if trimmed == "rust-version.workspace = true" {
773 result.push_str("rust-version = \"1.75\"");
774 } else if trimmed == "authors.workspace = true"
775 || trimmed == "repository.workspace = true"
776 || trimmed == "homepage.workspace = true"
777 || trimmed == "documentation.workspace = true"
778 || trimmed == "keywords.workspace = true"
779 || trimmed == "categories.workspace = true"
780 || trimmed == "license.workspace = true"
781 {
782 continue;
784 } else if trimmed.contains(".workspace = true") {
785 continue;
787 } else {
788 result.push_str(line);
789 }
790 result.push('\n');
791 }
792
793 fs::write(cargo_toml_path, result)
794 .map_err(|e| CompileError::Io(e.to_string()))?;
795
796 Ok(())
797}
798
799fn find_workspace_root() -> Result<std::path::PathBuf, CompileError> {
801 if let Ok(workspace) = std::env::var("LOGOS_WORKSPACE") {
803 let path = Path::new(&workspace);
804 if path.join("Cargo.toml").exists() && path.join("crates").exists() {
805 return Ok(path.to_path_buf());
806 }
807 }
808
809 if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
811 let path = Path::new(&manifest_dir);
812 if let Some(parent) = path.parent().and_then(|p| p.parent()) {
813 if parent.join("Cargo.toml").exists() {
814 return Ok(parent.to_path_buf());
815 }
816 }
817 }
818
819 if let Ok(exe) = std::env::current_exe() {
822 if let Some(dir) = exe.parent() {
823 let mut candidate = dir.to_path_buf();
825 for _ in 0..5 {
826 if candidate.join("Cargo.toml").exists() && candidate.join("crates").exists() {
827 return Ok(candidate);
828 }
829 if !candidate.pop() {
830 break;
831 }
832 }
833 }
834 }
835
836 let mut current = std::env::current_dir()
838 .map_err(|e| CompileError::Io(e.to_string()))?;
839
840 loop {
841 if current.join("Cargo.toml").exists() && current.join("crates").exists() {
842 return Ok(current);
843 }
844 if !current.pop() {
845 return Err(CompileError::Io(
846 "Could not find workspace root. Set LOGOS_WORKSPACE env var or run from within the workspace.".to_string()
847 ));
848 }
849 }
850}
851
852fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), CompileError> {
855 fs::create_dir_all(dst).map_err(|e| CompileError::Io(e.to_string()))?;
856
857 for entry in fs::read_dir(src).map_err(|e| CompileError::Io(e.to_string()))? {
858 let entry = entry.map_err(|e| CompileError::Io(e.to_string()))?;
859 let src_path = entry.path();
860 let file_name = entry.file_name();
861 let dst_path = dst.join(&file_name);
862
863 if file_name == "target"
865 || file_name == ".git"
866 || file_name == "Cargo.lock"
867 || file_name == ".DS_Store"
868 {
869 continue;
870 }
871
872 if file_name.to_string_lossy().starts_with('.') {
874 continue;
875 }
876
877 if !src_path.exists() {
879 continue;
880 }
881
882 if src_path.is_dir() {
883 copy_dir_recursive(&src_path, &dst_path)?;
884 } else if file_name == "Cargo.toml" {
885 match fs::read_to_string(&src_path) {
888 Ok(content) => {
889 let filtered: String = content
890 .lines()
891 .filter(|line| !line.trim().starts_with("[workspace]"))
892 .collect::<Vec<_>>()
893 .join("\n");
894 fs::write(&dst_path, filtered)
895 .map_err(|e| CompileError::Io(e.to_string()))?;
896 }
897 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
898 Err(e) => return Err(CompileError::Io(e.to_string())),
899 }
900 } else {
901 match fs::copy(&src_path, &dst_path) {
902 Ok(_) => {}
903 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
904 Err(e) => return Err(CompileError::Io(e.to_string())),
905 }
906 }
907 }
908
909 Ok(())
910}
911
912pub fn compile_and_run(source: &str, output_dir: &Path) -> Result<String, CompileError> {
955 compile_to_rust_checked(source).map_err(CompileError::Parse)?;
958
959 compile_to_dir(source, output_dir)?;
960
961 let build_output = Command::new("cargo")
963 .arg("build")
964 .arg("--message-format=json")
965 .current_dir(output_dir)
966 .output()
967 .map_err(|e| CompileError::Io(e.to_string()))?;
968
969 if !build_output.status.success() {
970 let stderr = String::from_utf8_lossy(&build_output.stderr);
971 let stdout = String::from_utf8_lossy(&build_output.stdout);
972
973 let diagnostics = parse_rustc_json(&stdout);
975
976 if !diagnostics.is_empty() {
977 let source_map = SourceMap::new(source.to_string());
979 let interner = Interner::new();
980
981 if let Some(logos_error) = translate_diagnostics(&diagnostics, &source_map, &interner) {
982 return Err(CompileError::Ownership(logos_error));
983 }
984 }
985
986 return Err(CompileError::Build(stderr.to_string()));
988 }
989
990 let run_output = Command::new("cargo")
992 .arg("run")
993 .arg("--quiet")
994 .current_dir(output_dir)
995 .output()
996 .map_err(|e| CompileError::Io(e.to_string()))?;
997
998 if !run_output.status.success() {
999 let stderr = String::from_utf8_lossy(&run_output.stderr);
1000 return Err(CompileError::Runtime(stderr.to_string()));
1001 }
1002
1003 let stdout = String::from_utf8_lossy(&run_output.stdout);
1004 Ok(stdout.to_string())
1005}
1006
1007pub fn compile_file(path: &Path) -> Result<String, CompileError> {
1010 let source = fs::read_to_string(path).map_err(|e| CompileError::Io(e.to_string()))?;
1011 compile_to_rust(&source).map_err(CompileError::Parse)
1012}
1013
1014pub fn compile_project(entry_file: &Path) -> Result<CompileOutput, CompileError> {
1032 use crate::loader::Loader;
1033 use crate::analysis::discover_with_imports;
1034
1035 let root_path = entry_file.parent().unwrap_or(Path::new(".")).to_path_buf();
1036 let mut loader = Loader::new(root_path);
1037 let mut interner = Interner::new();
1038
1039 let source = fs::read_to_string(entry_file)
1041 .map_err(|e| CompileError::Io(format!("Failed to read entry file: {}", e)))?;
1042
1043 let type_registry = discover_with_imports(entry_file, &source, &mut loader, &mut interner)
1045 .map_err(|e| CompileError::Io(e))?;
1046
1047 compile_to_rust_with_registry_full(&source, type_registry, &mut interner)
1049 .map_err(CompileError::Parse)
1050}
1051
1052fn compile_to_rust_with_registry_full(
1055 source: &str,
1056 type_registry: crate::analysis::TypeRegistry,
1057 interner: &mut Interner,
1058) -> Result<CompileOutput, ParseError> {
1059 let mut lexer = Lexer::new(source, interner);
1060 let tokens = lexer.tokenize();
1061
1062 let policy_registry = {
1064 let mut discovery = DiscoveryPass::new(&tokens, interner);
1065 discovery.run_full().policies
1066 };
1067
1068 let codegen_registry = type_registry.clone();
1069 let codegen_policies = policy_registry.clone();
1070
1071 let mut world_state = WorldState::new();
1072 let expr_arena = Arena::new();
1073 let term_arena = Arena::new();
1074 let np_arena = Arena::new();
1075 let sym_arena = Arena::new();
1076 let role_arena = Arena::new();
1077 let pp_arena = Arena::new();
1078 let stmt_arena: Arena<Stmt> = Arena::new();
1079 let imperative_expr_arena: Arena<Expr> = Arena::new();
1080 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1081
1082 let ast_ctx = AstContext::with_types(
1083 &expr_arena,
1084 &term_arena,
1085 &np_arena,
1086 &sym_arena,
1087 &role_arena,
1088 &pp_arena,
1089 &stmt_arena,
1090 &imperative_expr_arena,
1091 &type_expr_arena,
1092 );
1093
1094 let mut parser = Parser::new(tokens, &mut world_state, interner, ast_ctx, type_registry);
1095 let stmts = parser.parse_program()?;
1096
1097 let mut dependencies = extract_dependencies(&stmts, interner)?;
1099
1100 let needs_wasm_bindgen = stmts.iter().any(|stmt| {
1102 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
1103 interner.resolve(*target).eq_ignore_ascii_case("wasm")
1104 } else {
1105 false
1106 }
1107 });
1108 if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
1109 dependencies.push(CrateDependency {
1110 name: "wasm-bindgen".to_string(),
1111 version: "0.2".to_string(),
1112 features: vec![],
1113 });
1114 }
1115
1116 let mut escape_checker = EscapeChecker::new(interner);
1117 escape_checker.check_program(&stmts).map_err(|e| {
1118 ParseError {
1119 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
1120 span: e.span,
1121 }
1122 })?;
1123
1124 let type_env = crate::analysis::check_program(&stmts, interner, &codegen_registry)
1125 .map_err(|e| ParseError {
1126 kind: e.to_parse_error_kind(interner),
1127 span: crate::token::Span::default(),
1128 })?;
1129 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, interner, &type_env);
1130
1131 let has_c = stmts.iter().any(|stmt| {
1133 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
1134 match export_target {
1135 None => true,
1136 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1137 }
1138 } else {
1139 false
1140 }
1141 });
1142
1143 let c_header = if has_c {
1144 Some(generate_c_header(&stmts, "module", interner, &codegen_registry))
1145 } else {
1146 None
1147 };
1148
1149 if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
1150 dependencies.push(CrateDependency {
1151 name: "serde_json".to_string(),
1152 version: "1".to_string(),
1153 features: vec![],
1154 });
1155 }
1156
1157 let python_bindings = if has_c {
1158 Some(generate_python_bindings(&stmts, "module", interner, &codegen_registry))
1159 } else {
1160 None
1161 };
1162
1163 let (typescript_bindings, typescript_types) = if has_c {
1164 let (js, dts) = generate_typescript_bindings(&stmts, "module", interner, &codegen_registry);
1165 (Some(js), Some(dts))
1166 } else {
1167 (None, None)
1168 };
1169
1170 Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
1171}
1172
1173#[derive(Debug)]
1195pub enum CompileError {
1196 Parse(ParseError),
1201
1202 Io(String),
1206
1207 Build(String),
1212
1213 Runtime(String),
1217
1218 Ownership(LogosError),
1224}
1225
1226impl std::fmt::Display for CompileError {
1227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1228 match self {
1229 CompileError::Parse(e) => write!(f, "Parse error: {:?}", e),
1230 CompileError::Io(e) => write!(f, "IO error: {}", e),
1231 CompileError::Build(e) => write!(f, "Build error: {}", e),
1232 CompileError::Runtime(e) => write!(f, "Runtime error: {}", e),
1233 CompileError::Ownership(e) => write!(f, "{}", e),
1234 }
1235 }
1236}
1237
1238impl std::error::Error for CompileError {}
1239
1240pub fn encode_program_source(source: &str) -> Result<String, ParseError> {
1250 let full_source = if source.contains("## Main") || source.contains("## To ") {
1251 source.to_string()
1252 } else {
1253 format!("## Main\n{}", source)
1254 };
1255
1256 let mut interner = Interner::new();
1257 let mut lexer = Lexer::new(&full_source, &mut interner);
1258 let tokens = lexer.tokenize();
1259
1260 let type_registry = {
1261 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1262 let result = discovery.run_full();
1263 result.types
1264 };
1265
1266 let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
1268 for (_type_name, type_def) in type_registry.iter_types() {
1269 if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
1270 for variant in variants {
1271 let vname = interner.resolve(variant.name).to_string();
1272 let field_names: Vec<String> = variant.fields.iter()
1273 .map(|f| interner.resolve(f.name).to_string())
1274 .collect();
1275 variant_constructors.insert(vname, field_names);
1276 }
1277 }
1278 }
1279
1280 let mut world_state = WorldState::new();
1281 let expr_arena = Arena::new();
1282 let term_arena = Arena::new();
1283 let np_arena = Arena::new();
1284 let sym_arena = Arena::new();
1285 let role_arena = Arena::new();
1286 let pp_arena = Arena::new();
1287 let stmt_arena: Arena<Stmt> = Arena::new();
1288 let imperative_expr_arena: Arena<Expr> = Arena::new();
1289 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1290
1291 let ast_ctx = AstContext::with_types(
1292 &expr_arena, &term_arena, &np_arena, &sym_arena,
1293 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
1294 &type_expr_arena,
1295 );
1296
1297 let mut parser = crate::parser::Parser::new(
1298 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
1299 );
1300 let stmts = parser.parse_program()?;
1301
1302 let mut functions: Vec<(String, Vec<String>, Vec<&Stmt>)> = Vec::new();
1303 let mut main_stmts: Vec<&Stmt> = Vec::new();
1304
1305 for stmt in &stmts {
1306 if let Stmt::FunctionDef { name, params, body, 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 body_stmts: Vec<&Stmt> = body.iter().collect();
1316 functions.push((fn_name, param_names, body_stmts));
1317 } else {
1318 main_stmts.push(stmt);
1319 }
1320 }
1321
1322 let mut counter = 0usize;
1323 let mut output = String::new();
1324
1325 output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1327
1328 for (fn_name, params, body) in &functions {
1329 let body_var = encode_stmt_list_src(body, &mut counter, &mut output, &interner, &variant_constructors);
1330
1331 let params_var = format!("params_{}", counter);
1332 counter += 1;
1333 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1334 for p in params {
1335 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1336 }
1337
1338 let func_var = format!("func_{}", counter);
1339 counter += 1;
1340 output.push_str(&format!(
1341 "Let {} be a new CFuncDef with name \"{}\" and params {} and body {}.\n",
1342 func_var, fn_name, params_var, body_var
1343 ));
1344 output.push_str(&format!(
1345 "Set item \"{}\" of encodedFuncMap to {}.\n",
1346 fn_name, func_var
1347 ));
1348 }
1349
1350 let main_var = encode_stmt_list_src(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1352 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1353
1354 Ok(output)
1355}
1356
1357pub fn encode_program_source_compact(source: &str) -> Result<String, ParseError> {
1361 let full_source = if source.contains("## Main") || source.contains("## To ") {
1362 source.to_string()
1363 } else {
1364 format!("## Main\n{}", source)
1365 };
1366
1367 let mut interner = Interner::new();
1368 let mut lexer = Lexer::new(&full_source, &mut interner);
1369 let tokens = lexer.tokenize();
1370
1371 let type_registry = {
1372 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1373 let result = discovery.run_full();
1374 result.types
1375 };
1376
1377 let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
1378 for (_type_name, type_def) in type_registry.iter_types() {
1379 if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
1380 for variant in variants {
1381 let vname = interner.resolve(variant.name).to_string();
1382 let field_names: Vec<String> = variant.fields.iter()
1383 .map(|f| interner.resolve(f.name).to_string())
1384 .collect();
1385 variant_constructors.insert(vname, field_names);
1386 }
1387 }
1388 }
1389
1390 let mut world_state = WorldState::new();
1391 let expr_arena = Arena::new();
1392 let term_arena = Arena::new();
1393 let np_arena = Arena::new();
1394 let sym_arena = Arena::new();
1395 let role_arena = Arena::new();
1396 let pp_arena = Arena::new();
1397 let stmt_arena: Arena<Stmt> = Arena::new();
1398 let imperative_expr_arena: Arena<Expr> = Arena::new();
1399 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1400
1401 let ast_ctx = AstContext::with_types(
1402 &expr_arena, &term_arena, &np_arena, &sym_arena,
1403 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
1404 &type_expr_arena,
1405 );
1406
1407 let mut parser = crate::parser::Parser::new(
1408 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
1409 );
1410 let stmts = parser.parse_program()?;
1411
1412 let mut functions: Vec<(String, Vec<String>, Vec<&Stmt>)> = Vec::new();
1413 let mut main_stmts: Vec<&Stmt> = Vec::new();
1414
1415 for stmt in &stmts {
1416 if let Stmt::FunctionDef { name, params, body, is_native, .. } = stmt {
1417 if *is_native { continue; }
1418 let fn_name = interner.resolve(*name).to_string();
1419 let param_names: Vec<String> = params
1420 .iter()
1421 .map(|(name, _)| interner.resolve(*name).to_string())
1422 .collect();
1423 let body_stmts: Vec<&Stmt> = body.iter().collect();
1424 functions.push((fn_name, param_names, body_stmts));
1425 } else {
1426 main_stmts.push(stmt);
1427 }
1428 }
1429
1430 let mut counter = 0usize;
1431 let mut output = String::new();
1432
1433 output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1434
1435 for (fn_name, params, body) in &functions {
1436 let body_var = encode_stmt_list_compact(body, &mut counter, &mut output, &interner, &variant_constructors);
1437
1438 let params_var = format!("params_{}", counter);
1439 counter += 1;
1440 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1441 for p in params {
1442 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1443 }
1444
1445 let func_var = format!("func_{}", counter);
1446 counter += 1;
1447 output.push_str(&format!(
1448 "Let {} be a new CFuncDef with name \"{}\" and params {} and body {}.\n",
1449 func_var, fn_name, params_var, body_var
1450 ));
1451 output.push_str(&format!(
1452 "Set item \"{}\" of encodedFuncMap to {}.\n",
1453 fn_name, func_var
1454 ));
1455 }
1456
1457 let main_var = encode_stmt_list_compact(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1458 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1459
1460 Ok(output)
1461}
1462
1463fn try_inline_expr(expr: &Expr, interner: &Interner) -> Option<String> {
1466 match expr {
1467 Expr::Literal(lit) => match lit {
1468 Literal::Number(n) => Some(format!("(a new CInt with value {})", n)),
1469 Literal::Boolean(b) => Some(format!("(a new CBool with value {})", b)),
1470 Literal::Text(s) => {
1471 let text = interner.resolve(*s);
1472 Some(format!("(a new CText with value \"{}\")", text))
1473 }
1474 Literal::Float(f) => {
1475 let fs = format!("{}", f);
1476 let fs = if fs.contains('.') { fs } else { format!("{}.0", fs) };
1477 Some(format!("(a new CFloat with value {})", fs))
1478 }
1479 Literal::Nothing => Some("(a new CText with value \"nothing\")".to_string()),
1480 _ => None,
1481 },
1482 Expr::Identifier(sym) => {
1483 let name = interner.resolve(*sym);
1484 Some(format!("(a new CVar with name \"{}\")", name))
1485 }
1486 Expr::Not { operand } => {
1487 if let Some(inner) = try_inline_expr(operand, interner) {
1488 Some(format!("(a new CNot with inner {})", inner))
1489 } else {
1490 None
1491 }
1492 }
1493 Expr::OptionNone => Some("(a new COptionNone)".to_string()),
1494 _ => None,
1495 }
1496}
1497
1498fn encode_expr_compact(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1499 if let Some(inline) = try_inline_expr(expr, interner) {
1501 return inline;
1502 }
1503
1504 let var = format!("e_{}", *counter);
1506 *counter += 1;
1507
1508 match expr {
1509 Expr::BinaryOp { op, left, right } => {
1510 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1511 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1512 let op_str = match op {
1513 BinaryOpKind::Add => "+",
1514 BinaryOpKind::Subtract => "-",
1515 BinaryOpKind::Multiply => "*",
1516 BinaryOpKind::Divide => "/",
1517 BinaryOpKind::Modulo => "%",
1518 BinaryOpKind::Eq => "==",
1519 BinaryOpKind::NotEq => "!=",
1520 BinaryOpKind::Lt => "<",
1521 BinaryOpKind::Gt => ">",
1522 BinaryOpKind::LtEq => "<=",
1523 BinaryOpKind::GtEq => ">=",
1524 BinaryOpKind::And => "&&",
1525 BinaryOpKind::Or => "||",
1526 BinaryOpKind::Concat => "+",
1527 BinaryOpKind::BitXor => "^",
1528 BinaryOpKind::Shl => "<<",
1529 BinaryOpKind::Shr => ">>",
1530 };
1531 output.push_str(&format!(
1532 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
1533 var, op_str, left_var, right_var
1534 ));
1535 }
1536 Expr::Call { function, args } => {
1537 let fn_name = interner.resolve(*function);
1538 if let Some(field_names) = variants.get(fn_name) {
1539 let names_var = format!("nvNames_{}", *counter);
1540 *counter += 1;
1541 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1542 let vals_var = format!("nvVals_{}", *counter);
1543 *counter += 1;
1544 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1545 for (i, arg) in args.iter().enumerate() {
1546 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
1547 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1548 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1549 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
1550 }
1551 output.push_str(&format!(
1552 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1553 var, fn_name, names_var, vals_var
1554 ));
1555 } else {
1556 let args_var = format!("callArgs_{}", *counter);
1557 *counter += 1;
1558 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1559 for arg in args {
1560 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1561 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1562 }
1563 output.push_str(&format!(
1564 "Let {} be a new CCall with name \"{}\" and args {}.\n",
1565 var, fn_name, args_var
1566 ));
1567 }
1568 }
1569 Expr::Index { collection, index } => {
1570 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1571 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1572 output.push_str(&format!(
1573 "Let {} be a new CIndex with coll {} and idx {}.\n",
1574 var, coll_var, idx_var
1575 ));
1576 }
1577 Expr::Length { collection } => {
1578 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1579 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
1580 }
1581 Expr::FieldAccess { object, field } => {
1582 let obj_var = encode_expr_compact(object, counter, output, interner, variants);
1583 let field_name = interner.resolve(*field);
1584 output.push_str(&format!(
1585 "Let {} be a new CMapGet with target {} and key (a new CText with value \"{}\").\n",
1586 var, obj_var, field_name
1587 ));
1588 }
1589 Expr::NewVariant { variant, fields, .. } => {
1590 let variant_name = interner.resolve(*variant);
1591 let names_var = format!("nvNames_{}", *counter);
1592 *counter += 1;
1593 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1594 let vals_var = format!("nvVals_{}", *counter);
1595 *counter += 1;
1596 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1597 for (field_name, field_expr) in fields {
1598 let fname = interner.resolve(*field_name);
1599 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1600 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1601 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1602 }
1603 output.push_str(&format!(
1604 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1605 var, variant_name, names_var, vals_var
1606 ));
1607 }
1608 Expr::New { type_name, init_fields, .. } => {
1609 let tn = interner.resolve(*type_name);
1610 if tn == "Seq" || tn == "List" {
1611 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
1612 } else if tn == "Set" {
1613 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
1614 } else {
1615 let names_var = format!("nvNames_{}", *counter);
1616 *counter += 1;
1617 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1618 let vals_var = format!("nvVals_{}", *counter);
1619 *counter += 1;
1620 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1621 for (field_name, field_expr) in init_fields {
1622 let fname = interner.resolve(*field_name);
1623 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1624 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1625 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1626 }
1627 output.push_str(&format!(
1628 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1629 var, tn, names_var, vals_var
1630 ));
1631 }
1632 }
1633 Expr::InterpolatedString(parts) => {
1634 if parts.is_empty() {
1635 output.push_str(&format!("Let {} be (a new CText with value \"\").\n", var));
1636 } else {
1637 let mut part_vars: Vec<String> = Vec::new();
1638 for part in parts {
1639 match part {
1640 StringPart::Literal(sym) => {
1641 let text = interner.resolve(*sym);
1642 part_vars.push(format!("(a new CText with value \"{}\")", text));
1643 }
1644 StringPart::Expr { value, .. } => {
1645 let pv = encode_expr_compact(value, counter, output, interner, variants);
1646 part_vars.push(pv);
1647 }
1648 }
1649 }
1650 if part_vars.len() == 1 {
1651 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
1652 } else {
1653 let mut acc = part_vars[0].clone();
1654 for pv in &part_vars[1..] {
1655 let concat_var = format!("e_{}", *counter);
1656 *counter += 1;
1657 output.push_str(&format!(
1658 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
1659 concat_var, acc, pv
1660 ));
1661 acc = concat_var;
1662 }
1663 output.push_str(&format!("Let {} be {}.\n", var, acc));
1664 }
1665 }
1666 }
1667 Expr::Range { start, end } => {
1668 let start_var = encode_expr_compact(start, counter, output, interner, variants);
1669 let end_var = encode_expr_compact(end, counter, output, interner, variants);
1670 output.push_str(&format!(
1671 "Let {} be a new CRange with start {} and end {}.\n",
1672 var, start_var, end_var
1673 ));
1674 }
1675 Expr::Copy { expr } => {
1676 let inner_var = encode_expr_compact(expr, counter, output, interner, variants);
1677 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
1678 }
1679 Expr::Contains { collection, value } => {
1680 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1681 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1682 output.push_str(&format!(
1683 "Let {} be a new CContains with coll {} and elem {}.\n",
1684 var, coll_var, val_var
1685 ));
1686 }
1687 Expr::OptionSome { value } => {
1688 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1689 output.push_str(&format!(
1690 "Let {} be a new COptionSome with inner {}.\n", var, inner_var
1691 ));
1692 }
1693 Expr::Tuple(elems) => {
1694 let items_var = format!("tupItems_{}", *counter);
1695 *counter += 1;
1696 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
1697 for elem in elems {
1698 let elem_var = encode_expr_compact(elem, counter, output, interner, variants);
1699 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
1700 }
1701 output.push_str(&format!(
1702 "Let {} be a new CTuple with items {}.\n", var, items_var
1703 ));
1704 }
1705 Expr::Closure { params, body, .. } => {
1706 let params_var = format!("clp_{}", *counter);
1707 *counter += 1;
1708 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1709 let mut param_names = HashSet::new();
1710 for (sym, _) in params {
1711 let name = interner.resolve(*sym);
1712 param_names.insert(name.to_string());
1713 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
1714 }
1715 let body_var = format!("clb_{}", *counter);
1716 *counter += 1;
1717 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
1718 match body {
1719 ClosureBody::Expression(e) => {
1720 let ret_expr = encode_expr_compact(e, counter, output, interner, variants);
1721 let ret_var = format!("s_{}", *counter);
1722 *counter += 1;
1723 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
1724 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
1725 }
1726 ClosureBody::Block(stmts) => {
1727 for s in stmts.iter() {
1728 let sv = encode_stmt_compact(s, counter, output, interner, variants);
1729 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
1730 }
1731 }
1732 }
1733 let bound: HashSet<String> = param_names;
1734 let free = collect_free_vars_expr(expr, interner, &bound);
1735 let cap_var = format!("clc_{}", *counter);
1736 *counter += 1;
1737 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
1738 for fv in &free {
1739 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
1740 }
1741 output.push_str(&format!(
1742 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
1743 var, params_var, body_var, cap_var
1744 ));
1745 }
1746 Expr::CallExpr { callee, args } => {
1747 let callee_var = encode_expr_compact(callee, counter, output, interner, variants);
1748 let args_var = format!("cea_{}", *counter);
1749 *counter += 1;
1750 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1751 for a in args {
1752 let av = encode_expr_compact(a, counter, output, interner, variants);
1753 output.push_str(&format!("Push {} to {}.\n", av, args_var));
1754 }
1755 output.push_str(&format!(
1756 "Let {} be a new CCallExpr with target {} and args {}.\n",
1757 var, callee_var, args_var
1758 ));
1759 }
1760 Expr::Slice { collection, start, end } => {
1761 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1762 let start_var = encode_expr_compact(start, counter, output, interner, variants);
1763 let end_var = encode_expr_compact(end, counter, output, interner, variants);
1764 output.push_str(&format!(
1765 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
1766 var, coll_var, start_var, end_var
1767 ));
1768 }
1769 Expr::Union { left, right } => {
1770 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1771 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1772 output.push_str(&format!(
1773 "Let {} be a new CUnion with left {} and right {}.\n",
1774 var, left_var, right_var
1775 ));
1776 }
1777 Expr::Intersection { left, right } => {
1778 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1779 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1780 output.push_str(&format!(
1781 "Let {} be a new CIntersection with left {} and right {}.\n",
1782 var, left_var, right_var
1783 ));
1784 }
1785 Expr::Give { value } => {
1786 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1787 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
1788 }
1789 Expr::Escape { code, .. } => {
1790 let code_str = interner.resolve(*code);
1791 output.push_str(&format!(
1792 "Let {} be a new CEscExpr with code \"{}\".\n",
1793 var, code_str.replace('\"', "\\\"")
1794 ));
1795 }
1796 _ => {
1797 output.push_str(&format!("Let {} be (a new CText with value \"unsupported\").\n", var));
1799 }
1800 }
1801
1802 var
1803}
1804
1805fn encode_stmt_compact(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1806 let var = format!("s_{}", *counter);
1807 *counter += 1;
1808
1809 match stmt {
1810 Stmt::Let { var: name, value, .. } => {
1811 let name_str = interner.resolve(*name);
1812 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1813 output.push_str(&format!(
1814 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
1815 var, name_str, expr_var
1816 ));
1817 }
1818 Stmt::Set { target, value } => {
1819 let name_str = interner.resolve(*target);
1820 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1821 output.push_str(&format!(
1822 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
1823 var, name_str, expr_var
1824 ));
1825 }
1826 Stmt::If { cond, then_block, else_block } => {
1827 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1828 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
1829 let then_var = encode_stmt_list_compact(&then_stmts, counter, output, interner, variants);
1830 let else_var = if let Some(els) = else_block {
1831 let else_stmts: Vec<&Stmt> = els.iter().collect();
1832 encode_stmt_list_compact(&else_stmts, counter, output, interner, variants)
1833 } else {
1834 let empty_var = format!("emptyBlock_{}", *counter);
1835 *counter += 1;
1836 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
1837 empty_var
1838 };
1839 output.push_str(&format!(
1840 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
1841 var, cond_var, then_var, else_var
1842 ));
1843 }
1844 Stmt::While { cond, body, .. } => {
1845 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1846 let body_stmts: Vec<&Stmt> = body.iter().collect();
1847 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1848 output.push_str(&format!(
1849 "Let {} be a new CWhile with cond {} and body {}.\n",
1850 var, cond_var, body_var
1851 ));
1852 }
1853 Stmt::Return { value } => {
1854 if let Some(val) = value {
1855 let expr_var = encode_expr_compact(val, counter, output, interner, variants);
1856 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
1857 } else {
1858 output.push_str(&format!("Let {} be a new CReturn with expr (a new CText with value \"nothing\").\n", var));
1859 }
1860 }
1861 Stmt::Show { object, .. } => {
1862 let expr_var = encode_expr_compact(object, counter, output, interner, variants);
1863 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
1864 }
1865 Stmt::Repeat { pattern, iterable, body } => {
1866 let var_str = match pattern {
1867 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
1868 Pattern::Tuple(syms) => {
1869 if let Some(s) = syms.first() {
1870 interner.resolve(*s).to_string()
1871 } else {
1872 "item".to_string()
1873 }
1874 }
1875 };
1876 let coll_var = encode_expr_compact(iterable, counter, output, interner, variants);
1877 let body_stmts: Vec<&Stmt> = body.iter().collect();
1878 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1879 output.push_str(&format!(
1880 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
1881 var, var_str, coll_var, body_var
1882 ));
1883 }
1884 Stmt::Push { value, collection } => {
1885 let coll_name = extract_ident_name(collection, interner);
1886 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1887 output.push_str(&format!(
1888 "Let {} be a new CPush with expr {} and target \"{}\".\n",
1889 var, expr_var, coll_name
1890 ));
1891 }
1892 Stmt::SetIndex { collection, index, value } => {
1893 let target_str = extract_ident_name(collection, interner);
1894 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1895 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1896 output.push_str(&format!(
1897 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
1898 var, target_str, idx_var, val_var
1899 ));
1900 }
1901 Stmt::SetField { object, field, value } => {
1902 let target_str = extract_ident_name(object, interner);
1903 let field_str = interner.resolve(*field);
1904 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1905 output.push_str(&format!(
1906 "Let {} be a new CSetField with target \"{}\" and field \"{}\" and val {}.\n",
1907 var, target_str, field_str, val_var
1908 ));
1909 }
1910 Stmt::Break => {
1911 output.push_str(&format!("Let {} be a new CBreak.\n", var));
1912 }
1913 Stmt::Inspect { target, arms, .. } => {
1914 let target_var = encode_expr_compact(target, counter, output, interner, variants);
1915 let arms_var = format!("arms_{}", *counter);
1916 *counter += 1;
1917 output.push_str(&format!("Let {} be a new Seq of CMatchArm.\n", arms_var));
1918 for arm in arms {
1919 if let Some(variant_sym) = arm.variant {
1920 let vname = interner.resolve(variant_sym);
1921 let bindings_var = format!("bindings_{}", *counter);
1922 *counter += 1;
1923 output.push_str(&format!("Let {} be a new Seq of Text.\n", bindings_var));
1924 for (_, binding_name) in &arm.bindings {
1925 let bn = interner.resolve(*binding_name);
1926 output.push_str(&format!("Push \"{}\" to {}.\n", bn, bindings_var));
1927 }
1928 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1929 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1930 let arm_var = format!("arm_{}", *counter);
1931 *counter += 1;
1932 output.push_str(&format!(
1933 "Let {} be a new CWhen with variantName \"{}\" and bindings {} and body {}.\n",
1934 arm_var, vname, bindings_var, body_var
1935 ));
1936 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1937 } else {
1938 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1939 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1940 let arm_var = format!("arm_{}", *counter);
1941 *counter += 1;
1942 output.push_str(&format!(
1943 "Let {} be a new COtherwise with body {}.\n",
1944 arm_var, body_var
1945 ));
1946 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1947 }
1948 }
1949 output.push_str(&format!(
1950 "Let {} be a new CInspect with target {} and arms {}.\n",
1951 var, target_var, arms_var
1952 ));
1953 }
1954 _ => {
1955 return encode_stmt_src(stmt, counter, output, interner, variants);
1957 }
1958 }
1959
1960 var
1961}
1962
1963fn encode_stmt_list_compact(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1964 let list_var = format!("stmts_{}", *counter);
1965 *counter += 1;
1966 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
1967 for s in stmts {
1968 let sv = encode_stmt_compact(s, counter, output, interner, variants);
1969 output.push_str(&format!("Push {} to {}.\n", sv, list_var));
1970 }
1971 list_var
1972}
1973
1974fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1975 let mut free = HashSet::new();
1976 match expr {
1977 Expr::Identifier(sym) => {
1978 let name = interner.resolve(*sym).to_string();
1979 if !bound.contains(&name) {
1980 free.insert(name);
1981 }
1982 }
1983 Expr::BinaryOp { left, right, .. } => {
1984 free.extend(collect_free_vars_expr(left, interner, bound));
1985 free.extend(collect_free_vars_expr(right, interner, bound));
1986 }
1987 Expr::Not { operand } => {
1988 free.extend(collect_free_vars_expr(operand, interner, bound));
1989 }
1990 Expr::Copy { expr: inner } => {
1991 free.extend(collect_free_vars_expr(inner, interner, bound));
1992 }
1993 Expr::CallExpr { callee, args } => {
1994 free.extend(collect_free_vars_expr(callee, interner, bound));
1995 for a in args {
1996 free.extend(collect_free_vars_expr(a, interner, bound));
1997 }
1998 }
1999 Expr::Index { collection, index } => {
2000 free.extend(collect_free_vars_expr(collection, interner, bound));
2001 free.extend(collect_free_vars_expr(index, interner, bound));
2002 }
2003 Expr::InterpolatedString(parts) => {
2004 for part in parts {
2005 if let StringPart::Expr { value, .. } = part {
2006 free.extend(collect_free_vars_expr(value, interner, bound));
2007 }
2008 }
2009 }
2010 Expr::Closure { params, body, .. } => {
2011 let mut inner_bound = bound.clone();
2012 for (sym, _) in params {
2013 inner_bound.insert(interner.resolve(*sym).to_string());
2014 }
2015 match body {
2016 ClosureBody::Expression(e) => {
2017 free.extend(collect_free_vars_expr(e, interner, &inner_bound));
2018 }
2019 ClosureBody::Block(stmts) => {
2020 for s in stmts.iter() {
2021 free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
2022 }
2023 }
2024 }
2025 }
2026 _ => {}
2027 }
2028 free
2029}
2030
2031fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
2032 let mut free = HashSet::new();
2033 match stmt {
2034 Stmt::Let { var, value, .. } => {
2035 free.extend(collect_free_vars_expr(value, interner, bound));
2036 }
2037 Stmt::Set { target, value, .. } => {
2038 let n = interner.resolve(*target).to_string();
2039 if !bound.contains(&n) {
2040 free.insert(n);
2041 }
2042 free.extend(collect_free_vars_expr(value, interner, bound));
2043 }
2044 Stmt::Show { object, .. } => {
2045 free.extend(collect_free_vars_expr(object, interner, bound));
2046 }
2047 Stmt::Return { value } => {
2048 if let Some(v) = value {
2049 free.extend(collect_free_vars_expr(v, interner, bound));
2050 }
2051 }
2052 _ => {}
2053 }
2054 free
2055}
2056
2057fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2058 let var = format!("e_{}", *counter);
2059 *counter += 1;
2060
2061 match expr {
2062 Expr::Literal(lit) => match lit {
2063 Literal::Number(n) => {
2064 output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
2065 }
2066 Literal::Boolean(b) => {
2067 output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
2068 }
2069 Literal::Text(s) => {
2070 let text = interner.resolve(*s);
2071 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
2072 }
2073 Literal::Float(f) => {
2074 output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, f));
2075 }
2076 Literal::Duration(nanos) => {
2077 let millis = nanos / 1_000_000;
2078 let amount_var = format!("e_{}", *counter);
2079 *counter += 1;
2080 output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
2081 output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
2082 }
2083 Literal::Nothing => {
2084 output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
2085 }
2086 _ => {
2087 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2088 }
2089 },
2090 Expr::Identifier(sym) => {
2091 let name = interner.resolve(*sym);
2092 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
2093 }
2094 Expr::BinaryOp { op, left, right } => {
2095 let left_var = encode_expr_src(left, counter, output, interner, variants);
2096 let right_var = encode_expr_src(right, counter, output, interner, variants);
2097 let op_str = match op {
2098 BinaryOpKind::Add => "+",
2099 BinaryOpKind::Subtract => "-",
2100 BinaryOpKind::Multiply => "*",
2101 BinaryOpKind::Divide => "/",
2102 BinaryOpKind::Modulo => "%",
2103 BinaryOpKind::Eq => "==",
2104 BinaryOpKind::NotEq => "!=",
2105 BinaryOpKind::Lt => "<",
2106 BinaryOpKind::Gt => ">",
2107 BinaryOpKind::LtEq => "<=",
2108 BinaryOpKind::GtEq => ">=",
2109 BinaryOpKind::And => "&&",
2110 BinaryOpKind::Or => "||",
2111 BinaryOpKind::Concat => "+",
2112 BinaryOpKind::BitXor => "^",
2113 BinaryOpKind::Shl => "<<",
2114 BinaryOpKind::Shr => ">>",
2115 };
2116 output.push_str(&format!(
2117 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
2118 var, op_str, left_var, right_var
2119 ));
2120 }
2121 Expr::Not { operand } => {
2122 let inner_var = encode_expr_src(operand, counter, output, interner, variants);
2123 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
2124 }
2125 Expr::Call { function, args } => {
2126 let fn_name = interner.resolve(*function);
2127 if let Some(field_names) = variants.get(fn_name) {
2128 let names_var = format!("nvNames_{}", *counter);
2130 *counter += 1;
2131 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2132 let vals_var = format!("nvVals_{}", *counter);
2133 *counter += 1;
2134 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2135 for (i, arg) in args.iter().enumerate() {
2136 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
2137 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2138 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2139 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
2140 }
2141 output.push_str(&format!(
2142 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2143 var, fn_name, names_var, vals_var
2144 ));
2145 } else {
2146 let args_var = format!("callArgs_{}", *counter);
2147 *counter += 1;
2148 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2149 for arg in args {
2150 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2151 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2152 }
2153 output.push_str(&format!(
2154 "Let {} be a new CCall with name \"{}\" and args {}.\n",
2155 var, fn_name, args_var
2156 ));
2157 }
2158 }
2159 Expr::Index { collection, index } => {
2160 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2161 let idx_var = encode_expr_src(index, counter, output, interner, variants);
2162 output.push_str(&format!(
2163 "Let {} be a new CIndex with coll {} and idx {}.\n",
2164 var, coll_var, idx_var
2165 ));
2166 }
2167 Expr::Length { collection } => {
2168 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2169 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
2170 }
2171 Expr::FieldAccess { object, field } => {
2172 let obj_var = encode_expr_src(object, counter, output, interner, variants);
2173 let field_name = interner.resolve(*field);
2174 let key_var = format!("e_{}", *counter);
2175 *counter += 1;
2176 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2177 output.push_str(&format!(
2178 "Let {} be a new CMapGet with target {} and key {}.\n",
2179 var, obj_var, key_var
2180 ));
2181 }
2182 Expr::NewVariant { variant, fields, .. } => {
2183 let variant_name = interner.resolve(*variant);
2184 let names_var = format!("nvNames_{}", *counter);
2185 *counter += 1;
2186 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2187 let vals_var = format!("nvVals_{}", *counter);
2188 *counter += 1;
2189 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2190 for (field_name, field_expr) in fields {
2191 let fname = interner.resolve(*field_name);
2192 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2193 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2194 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2195 }
2196 output.push_str(&format!(
2197 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2198 var, variant_name, names_var, vals_var
2199 ));
2200 }
2201 Expr::New { type_name, init_fields, .. } => {
2202 let tn = interner.resolve(*type_name);
2203 if tn == "Seq" || tn == "List" {
2204 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
2205 } else if tn == "Set" {
2206 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
2207 } else if tn == "Map" || tn.starts_with("Map ") {
2208 let names_var = format!("nvNames_{}", *counter);
2210 *counter += 1;
2211 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2212 let vals_var = format!("nvVals_{}", *counter);
2213 *counter += 1;
2214 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2215 output.push_str(&format!(
2216 "Let {} be a new CNew with typeName \"Map\" and fieldNames {} and fields {}.\n",
2217 var, names_var, vals_var
2218 ));
2219 } else if init_fields.is_empty() {
2220 let names_var = format!("nvNames_{}", *counter);
2221 *counter += 1;
2222 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2223 let vals_var = format!("nvVals_{}", *counter);
2224 *counter += 1;
2225 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2226 output.push_str(&format!(
2227 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2228 var, tn, names_var, vals_var
2229 ));
2230 } else {
2231 let names_var = format!("nvNames_{}", *counter);
2232 *counter += 1;
2233 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2234 let vals_var = format!("nvVals_{}", *counter);
2235 *counter += 1;
2236 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2237 for (field_name, field_expr) in init_fields {
2238 let fname = interner.resolve(*field_name);
2239 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2240 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2241 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2242 }
2243 output.push_str(&format!(
2244 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2245 var, tn, names_var, vals_var
2246 ));
2247 }
2248 }
2249 Expr::InterpolatedString(parts) => {
2250 if parts.is_empty() {
2251 output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
2252 } else {
2253 let mut part_vars: Vec<String> = Vec::new();
2254 for part in parts {
2255 match part {
2256 StringPart::Literal(sym) => {
2257 let text = interner.resolve(*sym);
2258 let pv = format!("e_{}", *counter);
2259 *counter += 1;
2260 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", pv, text));
2261 part_vars.push(pv);
2262 }
2263 StringPart::Expr { value, .. } => {
2264 let pv = encode_expr_src(value, counter, output, interner, variants);
2265 part_vars.push(pv);
2266 }
2267 }
2268 }
2269 if part_vars.len() == 1 {
2270 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
2271 } else {
2272 let mut acc = part_vars[0].clone();
2273 for pv in &part_vars[1..] {
2274 let concat_var = format!("e_{}", *counter);
2275 *counter += 1;
2276 output.push_str(&format!(
2277 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
2278 concat_var, acc, pv
2279 ));
2280 acc = concat_var;
2281 }
2282 output.push_str(&format!("Let {} be {}.\n", var, acc));
2283 }
2284 }
2285 }
2286 Expr::Range { start, end } => {
2287 let start_var = encode_expr_src(start, counter, output, interner, variants);
2288 let end_var = encode_expr_src(end, counter, output, interner, variants);
2289 output.push_str(&format!(
2290 "Let {} be a new CRange with start {} and end {}.\n",
2291 var, start_var, end_var
2292 ));
2293 }
2294 Expr::Slice { collection, start, end } => {
2295 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2296 let start_var = encode_expr_src(start, counter, output, interner, variants);
2297 let end_var = encode_expr_src(end, counter, output, interner, variants);
2298 output.push_str(&format!(
2299 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
2300 var, coll_var, start_var, end_var
2301 ));
2302 }
2303 Expr::Copy { expr } => {
2304 let inner_var = encode_expr_src(expr, counter, output, interner, variants);
2305 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
2306 }
2307 Expr::Contains { collection, value } => {
2308 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2309 let val_var = encode_expr_src(value, counter, output, interner, variants);
2310 output.push_str(&format!(
2311 "Let {} be a new CContains with coll {} and elem {}.\n",
2312 var, coll_var, val_var
2313 ));
2314 }
2315 Expr::Union { left, right } => {
2316 let left_var = encode_expr_src(left, counter, output, interner, variants);
2317 let right_var = encode_expr_src(right, counter, output, interner, variants);
2318 output.push_str(&format!(
2319 "Let {} be a new CUnion with left {} and right {}.\n",
2320 var, left_var, right_var
2321 ));
2322 }
2323 Expr::Intersection { left, right } => {
2324 let left_var = encode_expr_src(left, counter, output, interner, variants);
2325 let right_var = encode_expr_src(right, counter, output, interner, variants);
2326 output.push_str(&format!(
2327 "Let {} be a new CIntersection with left {} and right {}.\n",
2328 var, left_var, right_var
2329 ));
2330 }
2331 Expr::OptionSome { value } => {
2332 let inner_var = encode_expr_src(value, counter, output, interner, variants);
2333 output.push_str(&format!(
2334 "Let {} be a new COptionSome with inner {}.\n",
2335 var, inner_var
2336 ));
2337 }
2338 Expr::OptionNone => {
2339 output.push_str(&format!("Let {} be a new COptionNone.\n", var));
2340 }
2341 Expr::Tuple(elems) => {
2342 let items_var = format!("tupItems_{}", *counter);
2343 *counter += 1;
2344 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2345 for elem in elems {
2346 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
2347 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2348 }
2349 output.push_str(&format!(
2350 "Let {} be a new CTuple with items {}.\n",
2351 var, items_var
2352 ));
2353 }
2354 Expr::Closure { params, body, .. } => {
2355 let params_var = format!("clp_{}", *counter);
2356 *counter += 1;
2357 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2358 let mut param_names = HashSet::new();
2359 for (sym, _) in params {
2360 let name = interner.resolve(*sym);
2361 param_names.insert(name.to_string());
2362 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
2363 }
2364 let body_var = format!("clb_{}", *counter);
2365 *counter += 1;
2366 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2367 match body {
2368 ClosureBody::Expression(e) => {
2369 let ret_expr = encode_expr_src(e, counter, output, interner, variants);
2370 let ret_var = format!("s_{}", *counter);
2371 *counter += 1;
2372 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
2373 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
2374 }
2375 ClosureBody::Block(stmts) => {
2376 for s in stmts.iter() {
2377 let sv = encode_stmt_src(s, counter, output, interner, variants);
2378 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2379 }
2380 }
2381 }
2382 let bound: HashSet<String> = param_names;
2383 let free = collect_free_vars_expr(expr, interner, &bound);
2384 let cap_var = format!("clc_{}", *counter);
2385 *counter += 1;
2386 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
2387 for fv in &free {
2388 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
2389 }
2390 output.push_str(&format!(
2391 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
2392 var, params_var, body_var, cap_var
2393 ));
2394 }
2395 Expr::CallExpr { callee, args } => {
2396 let callee_var = encode_expr_src(callee, counter, output, interner, variants);
2397 let args_var = format!("cea_{}", *counter);
2398 *counter += 1;
2399 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2400 for a in args {
2401 let av = encode_expr_src(a, counter, output, interner, variants);
2402 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2403 }
2404 output.push_str(&format!(
2405 "Let {} be a new CCallExpr with target {} and args {}.\n",
2406 var, callee_var, args_var
2407 ));
2408 }
2409 Expr::Give { value } => {
2410 let inner_var = encode_expr_src(value, counter, output, interner, variants);
2411 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
2412 }
2413 Expr::Escape { code, .. } => {
2414 let code_str = interner.resolve(*code);
2415 output.push_str(&format!(
2416 "Let {} be a new CEscExpr with code \"{}\".\n",
2417 var, code_str.replace('\"', "\\\"")
2418 ));
2419 }
2420 Expr::List(elems) => {
2421 let items_var = format!("litems_{}", *counter);
2422 *counter += 1;
2423 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2424 for elem in elems {
2425 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
2426 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2427 }
2428 output.push_str(&format!(
2429 "Let {} be a new CList with items {}.\n",
2430 var, items_var
2431 ));
2432 }
2433 _ => {
2434 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2435 }
2436 }
2437
2438 var
2439}
2440
2441fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2442 let var = format!("s_{}", *counter);
2443 *counter += 1;
2444
2445 match stmt {
2446 Stmt::Let { var: name, value, .. } => {
2447 let name_str = interner.resolve(*name);
2448 let expr_var = encode_expr_src(value, counter, output, interner, variants);
2449 output.push_str(&format!(
2450 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2451 var, name_str, expr_var
2452 ));
2453 }
2454 Stmt::Set { target, value } => {
2455 let name_str = interner.resolve(*target);
2456 let expr_var = encode_expr_src(value, counter, output, interner, variants);
2457 output.push_str(&format!(
2458 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2459 var, name_str, expr_var
2460 ));
2461 }
2462 Stmt::If { cond, then_block, else_block } => {
2463 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2464 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
2465 let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
2466 let else_var = if let Some(els) = else_block {
2467 let else_stmts: Vec<&Stmt> = els.iter().collect();
2468 encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
2469 } else {
2470 let empty_var = format!("emptyBlock_{}", *counter);
2471 *counter += 1;
2472 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
2473 empty_var
2474 };
2475 output.push_str(&format!(
2476 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2477 var, cond_var, then_var, else_var
2478 ));
2479 }
2480 Stmt::While { cond, body, .. } => {
2481 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2482 let body_stmts: Vec<&Stmt> = body.iter().collect();
2483 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
2484 output.push_str(&format!(
2485 "Let {} be a new CWhile with cond {} and body {}.\n",
2486 var, cond_var, body_var
2487 ));
2488 }
2489 Stmt::Return { value } => {
2490 if let Some(expr) = value {
2491 let expr_var = encode_expr_src(expr, counter, output, interner, variants);
2492 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
2493 } else {
2494 let nothing_var = format!("e_{}", *counter);
2495 *counter += 1;
2496 output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
2497 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
2498 }
2499 }
2500 Stmt::Show { object, .. } => {
2501 let expr_var = encode_expr_src(object, counter, output, interner, variants);
2502 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
2503 }
2504 Stmt::Call { function, args } => {
2505 let fn_name = interner.resolve(*function);
2506 let args_var = format!("callSArgs_{}", *counter);
2507 *counter += 1;
2508 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2509 for arg in args {
2510 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2511 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2512 }
2513 output.push_str(&format!(
2514 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2515 var, fn_name, args_var
2516 ));
2517 }
2518 Stmt::Push { value, collection } => {
2519 let val_var = encode_expr_src(value, counter, output, interner, variants);
2520 let coll_name = extract_ident_name(collection, interner);
2521 output.push_str(&format!(
2522 "Let {} be a new CPush with expr {} and target \"{}\".\n",
2523 var, val_var, coll_name
2524 ));
2525 }
2526 Stmt::SetIndex { collection, index, value } => {
2527 let coll_name = extract_ident_name(collection, interner);
2528 let idx_var = encode_expr_src(index, counter, output, interner, variants);
2529 let val_var = encode_expr_src(value, counter, output, interner, variants);
2530 output.push_str(&format!(
2531 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
2532 var, coll_name, idx_var, val_var
2533 ));
2534 }
2535 Stmt::SetField { object, field, value } => {
2536 let map_name = extract_ident_name(object, interner);
2537 let field_name = interner.resolve(*field);
2538 let key_var = format!("e_{}", *counter);
2539 *counter += 1;
2540 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2541 let val_var = encode_expr_src(value, counter, output, interner, variants);
2542 output.push_str(&format!(
2543 "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
2544 var, map_name, key_var, val_var
2545 ));
2546 }
2547 Stmt::Pop { collection, .. } => {
2548 let coll_name = extract_ident_name(collection, interner);
2549 output.push_str(&format!(
2550 "Let {} be a new CPop with target \"{}\".\n",
2551 var, coll_name
2552 ));
2553 }
2554 Stmt::Add { value, collection } => {
2555 let val_var = encode_expr_src(value, counter, output, interner, variants);
2556 let coll_name = extract_ident_name(collection, interner);
2557 output.push_str(&format!(
2558 "Let {} be a new CAdd with elem {} and target \"{}\".\n",
2559 var, val_var, coll_name
2560 ));
2561 }
2562 Stmt::Remove { value, collection } => {
2563 let val_var = encode_expr_src(value, counter, output, interner, variants);
2564 let coll_name = extract_ident_name(collection, interner);
2565 output.push_str(&format!(
2566 "Let {} be a new CRemove with elem {} and target \"{}\".\n",
2567 var, val_var, coll_name
2568 ));
2569 }
2570 Stmt::Inspect { .. } => {
2571 return String::new(); }
2573 Stmt::Repeat { .. } => {
2574 return String::new(); }
2576 Stmt::Break => {
2577 output.push_str(&format!("Let {} be a new CBreak.\n", var));
2578 }
2579 Stmt::RuntimeAssert { condition, .. } => {
2580 let cond_var = encode_expr_src(condition, counter, output, interner, variants);
2581 let msg_var = format!("e_{}", *counter);
2582 *counter += 1;
2583 output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
2584 output.push_str(&format!(
2585 "Let {} be a new CRuntimeAssert with cond {} and msg {}.\n",
2586 var, cond_var, msg_var
2587 ));
2588 }
2589 Stmt::Give { object, recipient } => {
2590 let expr_var = encode_expr_src(object, counter, output, interner, variants);
2591 let target_name = extract_ident_name(recipient, interner);
2592 output.push_str(&format!(
2593 "Let {} be a new CGive with expr {} and target \"{}\".\n",
2594 var, expr_var, target_name
2595 ));
2596 }
2597 Stmt::Escape { code, .. } => {
2598 let code_str = interner.resolve(*code);
2599 output.push_str(&format!(
2600 "Let {} be a new CEscStmt with code \"{}\".\n",
2601 var, code_str.replace('\"', "\\\"")
2602 ));
2603 }
2604 Stmt::Sleep { milliseconds } => {
2605 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2606 output.push_str(&format!(
2607 "Let {} be a new CSleep with duration {}.\n",
2608 var, dur_var
2609 ));
2610 }
2611 Stmt::ReadFrom { var: read_var, source } => {
2612 let var_name = interner.resolve(*read_var);
2613 match source {
2614 ReadSource::Console => {
2615 output.push_str(&format!(
2616 "Let {} be a new CReadConsole with target \"{}\".\n",
2617 var, var_name
2618 ));
2619 }
2620 ReadSource::File(path_expr) => {
2621 let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
2622 output.push_str(&format!(
2623 "Let {} be a new CReadFile with path {} and target \"{}\".\n",
2624 var, path_var, var_name
2625 ));
2626 }
2627 }
2628 }
2629 Stmt::WriteFile { content, path } => {
2630 let path_var = encode_expr_src(path, counter, output, interner, variants);
2631 let content_var = encode_expr_src(content, counter, output, interner, variants);
2632 output.push_str(&format!(
2633 "Let {} be a new CWriteFile with path {} and content {}.\n",
2634 var, path_var, content_var
2635 ));
2636 }
2637 Stmt::Check { source_text, .. } => {
2638 let pred_var = format!("e_{}", *counter);
2639 *counter += 1;
2640 output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
2641 let msg_var = format!("e_{}", *counter);
2642 *counter += 1;
2643 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
2644 output.push_str(&format!(
2645 "Let {} be a new CCheck with predicate {} and msg {}.\n",
2646 var, pred_var, msg_var
2647 ));
2648 }
2649 Stmt::Assert { .. } => {
2650 let prop_var = format!("e_{}", *counter);
2651 *counter += 1;
2652 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2653 output.push_str(&format!(
2654 "Let {} be a new CAssert with proposition {}.\n",
2655 var, prop_var
2656 ));
2657 }
2658 Stmt::Trust { justification, .. } => {
2659 let prop_var = format!("e_{}", *counter);
2660 *counter += 1;
2661 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2662 let just_str = interner.resolve(*justification);
2663 output.push_str(&format!(
2664 "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
2665 var, prop_var, just_str
2666 ));
2667 }
2668 Stmt::Require { crate_name, .. } => {
2669 let dep_name = interner.resolve(*crate_name);
2670 output.push_str(&format!(
2671 "Let {} be a new CRequire with dependency \"{}\".\n",
2672 var, dep_name
2673 ));
2674 }
2675 Stmt::MergeCrdt { source, target } => {
2676 let source_var = encode_expr_src(source, counter, output, interner, variants);
2677 let target_name = match target {
2678 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2679 _ => "unknown".to_string(),
2680 };
2681 output.push_str(&format!(
2682 "Let {} be a new CMerge with target \"{}\" and other {}.\n",
2683 var, target_name, source_var
2684 ));
2685 }
2686 Stmt::IncreaseCrdt { object, field, amount } => {
2687 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2688 let target_name = match object {
2689 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2690 _ => "unknown".to_string(),
2691 };
2692 output.push_str(&format!(
2693 "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
2694 var, target_name, amount_var
2695 ));
2696 }
2697 Stmt::DecreaseCrdt { object, field, amount } => {
2698 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2699 let target_name = match object {
2700 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2701 _ => "unknown".to_string(),
2702 };
2703 output.push_str(&format!(
2704 "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
2705 var, target_name, amount_var
2706 ));
2707 }
2708 Stmt::AppendToSequence { sequence, value } => {
2709 let value_var = encode_expr_src(value, counter, output, interner, variants);
2710 let target_name = match sequence {
2711 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2712 _ => "unknown".to_string(),
2713 };
2714 output.push_str(&format!(
2715 "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
2716 var, target_name, value_var
2717 ));
2718 }
2719 Stmt::ResolveConflict { object, .. } => {
2720 let target_name = match object {
2721 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2722 _ => "unknown".to_string(),
2723 };
2724 output.push_str(&format!(
2725 "Let {} be a new CResolve with target \"{}\".\n",
2726 var, target_name
2727 ));
2728 }
2729 Stmt::Sync { var: sync_var, topic } => {
2730 let topic_var = encode_expr_src(topic, counter, output, interner, variants);
2731 let var_name = interner.resolve(*sync_var);
2732 output.push_str(&format!(
2733 "Let {} be a new CSync with target \"{}\" and channel {}.\n",
2734 var, var_name, topic_var
2735 ));
2736 }
2737 Stmt::Mount { var: mount_var, path } => {
2738 let path_var = encode_expr_src(path, counter, output, interner, variants);
2739 let var_name = interner.resolve(*mount_var);
2740 output.push_str(&format!(
2741 "Let {} be a new CMount with target \"{}\" and path {}.\n",
2742 var, var_name, path_var
2743 ));
2744 }
2745 Stmt::Concurrent { tasks } => {
2746 let branches_var = format!("e_{}", *counter);
2747 *counter += 1;
2748 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2749 let branch_var = format!("e_{}", *counter);
2750 *counter += 1;
2751 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2752 for stmt in tasks.iter() {
2753 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2754 if !sv.is_empty() {
2755 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2756 }
2757 }
2758 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2759 output.push_str(&format!(
2760 "Let {} be a new CConcurrent with branches {}.\n",
2761 var, branches_var
2762 ));
2763 }
2764 Stmt::Parallel { tasks } => {
2765 let branches_var = format!("e_{}", *counter);
2766 *counter += 1;
2767 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2768 let branch_var = format!("e_{}", *counter);
2769 *counter += 1;
2770 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2771 for stmt in tasks.iter() {
2772 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2773 if !sv.is_empty() {
2774 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2775 }
2776 }
2777 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2778 output.push_str(&format!(
2779 "Let {} be a new CParallel with branches {}.\n",
2780 var, branches_var
2781 ));
2782 }
2783 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
2784 let func_name = interner.resolve(*function);
2785 let args_var = format!("e_{}", *counter);
2786 *counter += 1;
2787 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2788 for arg in args {
2789 let av = encode_expr_src(arg, counter, output, interner, variants);
2790 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2791 }
2792 let body_var = format!("e_{}", *counter);
2793 *counter += 1;
2794 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2795 let call_var = format!("e_{}", *counter);
2796 *counter += 1;
2797 output.push_str(&format!(
2798 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2799 call_var, func_name, args_var
2800 ));
2801 output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
2802 let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
2803 interner.resolve(*handle).to_string()
2804 } else {
2805 "_task".to_string()
2806 };
2807 output.push_str(&format!(
2808 "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
2809 var, body_var, handle_name
2810 ));
2811 }
2812 Stmt::StopTask { handle } => {
2813 let handle_var = encode_expr_src(handle, counter, output, interner, variants);
2814 output.push_str(&format!(
2815 "Let {} be a new CStopTask with handle {}.\n",
2816 var, handle_var
2817 ));
2818 }
2819 Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
2820 let cap = capacity.unwrap_or(32);
2821 let cap_var = format!("e_{}", *counter);
2822 *counter += 1;
2823 output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
2824 let pipe_name = interner.resolve(*pipe_var);
2825 output.push_str(&format!(
2826 "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
2827 var, pipe_name, cap_var
2828 ));
2829 }
2830 Stmt::SendPipe { value, pipe } => {
2831 let val_var = encode_expr_src(value, counter, output, interner, variants);
2832 let pipe_name = match pipe {
2833 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2834 _ => "pipe".to_string(),
2835 };
2836 output.push_str(&format!(
2837 "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
2838 var, pipe_name, val_var
2839 ));
2840 }
2841 Stmt::ReceivePipe { var: recv_var, pipe } => {
2842 let recv_name = interner.resolve(*recv_var);
2843 let pipe_name = match pipe {
2844 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2845 _ => "pipe".to_string(),
2846 };
2847 output.push_str(&format!(
2848 "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
2849 var, pipe_name, recv_name
2850 ));
2851 }
2852 Stmt::TrySendPipe { value, pipe, .. } => {
2853 let val_var = encode_expr_src(value, counter, output, interner, variants);
2854 let pipe_name = match pipe {
2855 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2856 _ => "pipe".to_string(),
2857 };
2858 output.push_str(&format!(
2859 "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
2860 var, pipe_name, val_var
2861 ));
2862 }
2863 Stmt::TryReceivePipe { var: recv_var, pipe } => {
2864 let recv_name = interner.resolve(*recv_var);
2865 let pipe_name = match pipe {
2866 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2867 _ => "pipe".to_string(),
2868 };
2869 output.push_str(&format!(
2870 "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
2871 var, pipe_name, recv_name
2872 ));
2873 }
2874 Stmt::Select { branches } => {
2875 let branches_var = format!("e_{}", *counter);
2876 *counter += 1;
2877 output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
2878 for branch in branches {
2879 match branch {
2880 SelectBranch::Receive { var: recv_var, pipe, body } => {
2881 let recv_name = interner.resolve(*recv_var);
2882 let pipe_name = match pipe {
2883 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2884 _ => "pipe".to_string(),
2885 };
2886 let body_var = format!("e_{}", *counter);
2887 *counter += 1;
2888 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2889 for stmt in body.iter() {
2890 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2891 if !sv.is_empty() {
2892 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2893 }
2894 }
2895 let branch_var = format!("e_{}", *counter);
2896 *counter += 1;
2897 output.push_str(&format!(
2898 "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
2899 branch_var, pipe_name, recv_name, body_var
2900 ));
2901 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2902 }
2903 SelectBranch::Timeout { milliseconds, body } => {
2904 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2905 let body_var = format!("e_{}", *counter);
2906 *counter += 1;
2907 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2908 for stmt in body.iter() {
2909 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2910 if !sv.is_empty() {
2911 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2912 }
2913 }
2914 let branch_var = format!("e_{}", *counter);
2915 *counter += 1;
2916 output.push_str(&format!(
2917 "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
2918 branch_var, dur_var, body_var
2919 ));
2920 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2921 }
2922 }
2923 }
2924 output.push_str(&format!(
2925 "Let {} be a new CSelect with branches {}.\n",
2926 var, branches_var
2927 ));
2928 }
2929 Stmt::Spawn { agent_type, name } => {
2930 let agent_name = interner.resolve(*agent_type);
2931 let target_name = interner.resolve(*name);
2932 output.push_str(&format!(
2933 "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
2934 var, agent_name, target_name
2935 ));
2936 }
2937 Stmt::SendMessage { message, destination } => {
2938 let target_var = encode_expr_src(destination, counter, output, interner, variants);
2939 let msg_var = encode_expr_src(message, counter, output, interner, variants);
2940 output.push_str(&format!(
2941 "Let {} be a new CSendMessage with target {} and msg {}.\n",
2942 var, target_var, msg_var
2943 ));
2944 }
2945 Stmt::AwaitMessage { into, .. } => {
2946 let await_name = interner.resolve(*into);
2947 output.push_str(&format!(
2948 "Let {} be a new CAwaitMessage with target \"{}\".\n",
2949 var, await_name
2950 ));
2951 }
2952 Stmt::Listen { address } => {
2953 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2954 output.push_str(&format!(
2955 "Let {} be a new CListen with addr {} and handler \"default\".\n",
2956 var, addr_var
2957 ));
2958 }
2959 Stmt::ConnectTo { address } => {
2960 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2961 output.push_str(&format!(
2962 "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
2963 var, addr_var
2964 ));
2965 }
2966 Stmt::Zone { name, body, .. } => {
2967 let zone_name = interner.resolve(*name);
2968 let body_var = format!("e_{}", *counter);
2969 *counter += 1;
2970 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2971 for stmt in body.iter() {
2972 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2973 if !sv.is_empty() {
2974 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2975 }
2976 }
2977 output.push_str(&format!(
2978 "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
2979 var, zone_name, body_var
2980 ));
2981 }
2982 Stmt::LetPeerAgent { var: pa_var, address } => {
2983 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2984 let pa_name = interner.resolve(*pa_var);
2985 output.push_str(&format!(
2986 "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
2987 var, addr_var, pa_name
2988 ));
2989 }
2990 _ => {
2991 return String::new();
2992 }
2993 }
2994
2995 var
2996}
2997
2998fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
2999 match stmt {
3000 Stmt::Inspect { target, arms, .. } => {
3001 let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
3002 let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
3003
3004 for arm in arms {
3005 if arm.variant.is_none() {
3006 otherwise_stmts = arm.body.iter().collect();
3007 } else {
3008 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
3009 variant_arms.push((arm, body_refs));
3010 }
3011 }
3012
3013 if variant_arms.is_empty() {
3014 let mut result = Vec::new();
3015 for s in &otherwise_stmts {
3016 for v in encode_stmts_src(s, counter, output, interner, variants) {
3017 result.push(v);
3018 }
3019 }
3020 return result;
3021 }
3022
3023 let has_otherwise = !otherwise_stmts.is_empty();
3028 let mut result = Vec::new();
3029
3030 let matched_var_name = if has_otherwise {
3032 let name = format!("__inspectMatched_{}", *counter);
3033 *counter += 1;
3034 let false_expr = format!("e_{}", *counter);
3035 *counter += 1;
3036 output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
3037 let let_stmt = format!("s_{}", *counter);
3038 *counter += 1;
3039 output.push_str(&format!(
3040 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3041 let_stmt, name, false_expr
3042 ));
3043 result.push(let_stmt);
3044 Some(name)
3045 } else {
3046 None
3047 };
3048
3049 for (arm, body_stmts) in &variant_arms {
3051 let variant_name = interner.resolve(arm.variant.unwrap());
3052
3053 let tag_target = encode_expr_src(target, counter, output, interner, variants);
3055 let tag_key = format!("e_{}", *counter);
3056 *counter += 1;
3057 output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
3058 let tag_get = format!("e_{}", *counter);
3059 *counter += 1;
3060 output.push_str(&format!(
3061 "Let {} be a new CMapGet with target {} and key {}.\n",
3062 tag_get, tag_target, tag_key
3063 ));
3064 let variant_text = format!("e_{}", *counter);
3065 *counter += 1;
3066 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
3067 let cond_var = format!("e_{}", *counter);
3068 *counter += 1;
3069 output.push_str(&format!(
3070 "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
3071 cond_var, tag_get, variant_text
3072 ));
3073
3074 let then_list = format!("stmtList_{}", *counter);
3076 *counter += 1;
3077 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
3078
3079 if let Some(ref mname) = matched_var_name {
3081 let true_expr = format!("e_{}", *counter);
3082 *counter += 1;
3083 output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
3084 let set_stmt = format!("s_{}", *counter);
3085 *counter += 1;
3086 output.push_str(&format!(
3087 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3088 set_stmt, mname, true_expr
3089 ));
3090 output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
3091 }
3092
3093 for (field_name, binding_name) in &arm.bindings {
3095 let field_str = interner.resolve(*field_name);
3096 let bind_str = interner.resolve(*binding_name);
3097 let bind_target = encode_expr_src(target, counter, output, interner, variants);
3098 let fkey = format!("e_{}", *counter);
3099 *counter += 1;
3100 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
3101 let fget = format!("e_{}", *counter);
3102 *counter += 1;
3103 output.push_str(&format!(
3104 "Let {} be a new CMapGet with target {} and key {}.\n",
3105 fget, bind_target, fkey
3106 ));
3107 let bind_let = format!("s_{}", *counter);
3108 *counter += 1;
3109 output.push_str(&format!(
3110 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3111 bind_let, bind_str, fget
3112 ));
3113 output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
3114 }
3115
3116 for body_stmt in body_stmts {
3118 match body_stmt {
3119 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
3120 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
3121 for v in vars {
3122 output.push_str(&format!("Push {} to {}.\n", v, then_list));
3123 }
3124 }
3125 _ => {
3126 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
3127 if !bvar.is_empty() {
3128 output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
3129 }
3130 }
3131 }
3132 }
3133
3134 let empty_else = format!("stmtList_{}", *counter);
3136 *counter += 1;
3137 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3138
3139 let if_var = format!("s_{}", *counter);
3141 *counter += 1;
3142 output.push_str(&format!(
3143 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3144 if_var, cond_var, then_list, empty_else
3145 ));
3146
3147 result.push(if_var);
3148 }
3149
3150 if let Some(ref mname) = matched_var_name {
3152 let matched_ref = format!("e_{}", *counter);
3153 *counter += 1;
3154 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
3155 let not_matched = format!("e_{}", *counter);
3156 *counter += 1;
3157 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
3158
3159 let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
3160 let empty_else = format!("stmtList_{}", *counter);
3161 *counter += 1;
3162 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3163
3164 let otherwise_if = format!("s_{}", *counter);
3165 *counter += 1;
3166 output.push_str(&format!(
3167 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3168 otherwise_if, not_matched, otherwise_block, empty_else
3169 ));
3170 result.push(otherwise_if);
3171 }
3172
3173 result
3174 }
3175 Stmt::Repeat { pattern, iterable, body, .. } => {
3176 let loop_var_name = match pattern {
3177 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3178 Pattern::Tuple(syms) => {
3179 if let Some(s) = syms.first() {
3180 interner.resolve(*s).to_string()
3181 } else {
3182 "item".to_string()
3183 }
3184 }
3185 };
3186
3187 if let Expr::Range { start, end } = iterable {
3189 let start_var = encode_expr_src(start, counter, output, interner, variants);
3190 let end_var = encode_expr_src(end, counter, output, interner, variants);
3191 let body_stmts: Vec<&Stmt> = body.iter().collect();
3192 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
3193 let rr = format!("s_{}", *counter);
3194 *counter += 1;
3195 output.push_str(&format!(
3196 "Let {} be a new CRepeatRange with var \"{}\" and start {} and end {} and body {}.\n",
3197 rr, loop_var_name, start_var, end_var, body_var
3198 ));
3199 return vec![rr];
3200 }
3201
3202 let coll_var = encode_expr_src(iterable, counter, output, interner, variants);
3204 let body_stmts: Vec<&Stmt> = body.iter().collect();
3205 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
3206 let rep = format!("s_{}", *counter);
3207 *counter += 1;
3208 output.push_str(&format!(
3209 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
3210 rep, loop_var_name, coll_var, body_var
3211 ));
3212 vec![rep]
3213 }
3214 _ => {
3215 let v = encode_stmt_src(stmt, counter, output, interner, variants);
3216 if v.is_empty() {
3217 vec![]
3218 } else {
3219 vec![v]
3220 }
3221 }
3222 }
3223}
3224
3225fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3226 let list_var = format!("stmtList_{}", *counter);
3227 *counter += 1;
3228 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
3229
3230 for stmt in stmts {
3231 for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
3232 output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
3233 }
3234 }
3235
3236 list_var
3237}
3238
3239fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
3240 match expr {
3241 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3242 _ => "unknown".to_string(),
3243 }
3244}
3245
3246pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
3259 let full_source = if program.contains("## Main") || program.contains("## To ") {
3260 program.to_string()
3261 } else {
3262 format!("## Main\n{}", program)
3263 };
3264
3265 let mut interner = Interner::new();
3266 let mut lexer = Lexer::new(&full_source, &mut interner);
3267 let tokens = lexer.tokenize();
3268
3269 let type_registry = {
3270 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3271 let result = discovery.run_full();
3272 result.types
3273 };
3274
3275 let mut world_state = WorldState::new();
3276 let expr_arena = Arena::new();
3277 let term_arena = Arena::new();
3278 let np_arena = Arena::new();
3279 let sym_arena = Arena::new();
3280 let role_arena = Arena::new();
3281 let pp_arena = Arena::new();
3282 let stmt_arena: Arena<Stmt> = Arena::new();
3283 let imperative_expr_arena: Arena<Expr> = Arena::new();
3284 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3285
3286 let ast_ctx = AstContext::with_types(
3287 &expr_arena, &term_arena, &np_arena, &sym_arena,
3288 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3289 &type_expr_arena,
3290 );
3291
3292 let mut parser = crate::parser::Parser::new(
3293 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3294 );
3295 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3296
3297 let optimized = crate::optimize::optimize_for_projection(
3302 stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
3303 );
3304
3305 let mut output = String::new();
3306
3307 for stmt in &optimized {
3308 if matches!(stmt, Stmt::FunctionDef { .. }) {
3309 decompile_stmt(stmt, &interner, &mut output, 0);
3310 output.push('\n');
3311 }
3312 }
3313
3314 output.push_str("## Main\n");
3315 for stmt in &optimized {
3316 if !matches!(stmt, Stmt::FunctionDef { .. }) {
3317 decompile_stmt(stmt, &interner, &mut output, 0);
3318 }
3319 }
3320
3321 Ok(output)
3322}
3323
3324fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
3325 let pad = " ".repeat(indent);
3326 match stmt {
3327 Stmt::FunctionDef { name, params, body, return_type, .. } => {
3328 let fn_name = interner.resolve(*name);
3329 let param_strs: Vec<String> = params
3330 .iter()
3331 .map(|(name, ty)| {
3332 let pname = interner.resolve(*name);
3333 format!("{}: {}", pname, decompile_type_expr(ty, interner))
3334 })
3335 .collect();
3336 let ret_str = if let Some(rt) = return_type {
3337 format!(" -> {}", decompile_type_expr(rt, interner))
3338 } else {
3339 String::new()
3340 };
3341 out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
3342 for s in body.iter() {
3343 decompile_stmt(s, interner, out, indent + 1);
3344 }
3345 }
3346 Stmt::Let { var, value, mutable, .. } => {
3347 let name = interner.resolve(*var);
3348 let expr_str = decompile_expr(value, interner);
3349 if *mutable {
3350 out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
3351 } else {
3352 out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
3353 }
3354 }
3355 Stmt::Set { target, value } => {
3356 let name = interner.resolve(*target);
3357 let expr_str = decompile_expr(value, interner);
3358 out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
3359 }
3360 Stmt::Show { object, .. } => {
3361 let expr_str = decompile_expr(object, interner);
3362 out.push_str(&format!("{}Show {}.\n", pad, expr_str));
3363 }
3364 Stmt::Return { value } => {
3365 if let Some(expr) = value {
3366 let expr_str = decompile_expr(expr, interner);
3367 out.push_str(&format!("{}Return {}.\n", pad, expr_str));
3368 } else {
3369 out.push_str(&format!("{}Return.\n", pad));
3370 }
3371 }
3372 Stmt::If { cond, then_block, else_block } => {
3373 let cond_str = decompile_expr(cond, interner);
3374 out.push_str(&format!("{}If {}:\n", pad, cond_str));
3375 for s in then_block.iter() {
3376 decompile_stmt(s, interner, out, indent + 1);
3377 }
3378 if let Some(els) = else_block {
3379 out.push_str(&format!("{}Otherwise:\n", pad));
3380 for s in els.iter() {
3381 decompile_stmt(s, interner, out, indent + 1);
3382 }
3383 }
3384 }
3385 Stmt::While { cond, body, .. } => {
3386 let cond_str = decompile_expr(cond, interner);
3387 out.push_str(&format!("{}While {}:\n", pad, cond_str));
3388 for s in body.iter() {
3389 decompile_stmt(s, interner, out, indent + 1);
3390 }
3391 }
3392 Stmt::Call { function, args } => {
3393 let fn_name = interner.resolve(*function);
3394 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3395 if arg_strs.is_empty() {
3396 out.push_str(&format!("{}{}().\n", pad, fn_name));
3397 } else {
3398 out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
3399 }
3400 }
3401 Stmt::Push { value, collection } => {
3402 let val_str = decompile_expr(value, interner);
3403 let coll_str = decompile_expr(collection, interner);
3404 out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
3405 }
3406 Stmt::SetIndex { collection, index, value } => {
3407 let coll_str = decompile_expr(collection, interner);
3408 let idx_str = decompile_expr(index, interner);
3409 let val_str = decompile_expr(value, interner);
3410 out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
3411 }
3412 Stmt::SetField { object, field, value } => {
3413 let obj_str = decompile_expr(object, interner);
3414 let field_name = interner.resolve(*field);
3415 let val_str = decompile_expr(value, interner);
3416 out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
3417 }
3418 Stmt::Repeat { pattern, iterable, body, .. } => {
3419 let var_name = match pattern {
3420 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3421 Pattern::Tuple(syms) => {
3422 syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
3423 }
3424 };
3425 let iter_str = decompile_expr(iterable, interner);
3426 out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
3427 for s in body.iter() {
3428 decompile_stmt(s, interner, out, indent + 1);
3429 }
3430 }
3431 Stmt::Inspect { target, arms, .. } => {
3432 let target_str = decompile_expr(target, interner);
3433 out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
3434 for arm in arms {
3435 if let Some(variant) = arm.variant {
3436 let variant_name = interner.resolve(variant);
3437 let bindings: Vec<String> = arm.bindings.iter()
3438 .map(|(_, b)| interner.resolve(*b).to_string())
3439 .collect();
3440 if bindings.is_empty() {
3441 out.push_str(&format!("{} When {}:\n", pad, variant_name));
3442 } else {
3443 out.push_str(&format!("{} When {}({}):\n", pad, variant_name, bindings.join(", ")));
3444 }
3445 } else {
3446 out.push_str(&format!("{} Otherwise:\n", pad));
3447 }
3448 for s in arm.body.iter() {
3449 decompile_stmt(s, interner, out, indent + 2);
3450 }
3451 }
3452 }
3453 Stmt::Pop { collection, into } => {
3454 let coll_str = decompile_expr(collection, interner);
3455 if let Some(target) = into {
3456 let target_name = interner.resolve(*target);
3457 out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
3458 } else {
3459 out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
3460 }
3461 }
3462 Stmt::Break => {
3463 out.push_str(&format!("{}Break.\n", pad));
3464 }
3465 Stmt::RuntimeAssert { condition } => {
3466 let cond_str = decompile_expr(condition, interner);
3467 out.push_str(&format!("{}Assert that {}.\n", pad, cond_str));
3468 }
3469 Stmt::Add { value, collection } => {
3470 let val_str = decompile_expr(value, interner);
3471 let coll_str = decompile_expr(collection, interner);
3472 out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
3473 }
3474 Stmt::Remove { value, collection } => {
3475 let val_str = decompile_expr(value, interner);
3476 let coll_str = decompile_expr(collection, interner);
3477 out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
3478 }
3479 Stmt::Zone { name, body, .. } => {
3480 let zone_name = interner.resolve(*name);
3481 out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
3482 for s in body.iter() {
3483 decompile_stmt(s, interner, out, indent + 1);
3484 }
3485 }
3486 Stmt::ReadFrom { var, .. } => {
3487 let var_name = interner.resolve(*var);
3488 out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
3489 }
3490 Stmt::WriteFile { content, path } => {
3491 let content_str = decompile_expr(content, interner);
3492 let path_str = decompile_expr(path, interner);
3493 out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
3494 }
3495 Stmt::Sleep { milliseconds } => {
3496 let ms = decompile_expr(milliseconds, interner);
3497 out.push_str(&format!("{}Sleep {}.\n", pad, ms));
3498 }
3499 _ => {
3500 }
3503 }
3504}
3505
3506fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
3507 match expr {
3508 Expr::Literal(lit) => match lit {
3509 Literal::Number(n) => n.to_string(),
3510 Literal::Float(f) => format!("{}", f),
3511 Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
3512 Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
3513 Literal::Nothing => "nothing".to_string(),
3514 Literal::Char(c) => format!("'{}'", c),
3515 Literal::Duration(ns) => format!("{}", ns),
3516 Literal::Date(days) => format!("{}", days),
3517 Literal::Moment(ns) => format!("{}", ns),
3518 Literal::Span { months, days } => format!("{} months {} days", months, days),
3519 Literal::Time(ns) => format!("{}", ns),
3520 },
3521 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3522 Expr::BinaryOp { op, left, right } => {
3523 let l = if matches!(left, Expr::BinaryOp { .. }) {
3524 format!("({})", decompile_expr(left, interner))
3525 } else {
3526 decompile_expr(left, interner)
3527 };
3528 let r = if matches!(right, Expr::BinaryOp { .. }) {
3529 format!("({})", decompile_expr(right, interner))
3530 } else {
3531 decompile_expr(right, interner)
3532 };
3533 if matches!(op, BinaryOpKind::Shl) {
3536 if let Expr::Literal(Literal::Number(k)) = right {
3538 let multiplier = 1i64 << k;
3539 return format!("{} * {}", l, multiplier);
3540 }
3541 }
3542 if matches!(op, BinaryOpKind::Shr) {
3543 if let Expr::Literal(Literal::Number(k)) = right {
3545 let divisor = 1i64 << k;
3546 return format!("{} / {}", l, divisor);
3547 }
3548 }
3549 let op_str = match op {
3550 BinaryOpKind::Add => "+",
3551 BinaryOpKind::Subtract => "-",
3552 BinaryOpKind::Multiply => "*",
3553 BinaryOpKind::Divide => "/",
3554 BinaryOpKind::Modulo => "%",
3555 BinaryOpKind::Eq => "equals",
3556 BinaryOpKind::NotEq => "is not",
3557 BinaryOpKind::Lt => "is less than",
3558 BinaryOpKind::Gt => "is greater than",
3559 BinaryOpKind::LtEq => "is at most",
3560 BinaryOpKind::GtEq => "is at least",
3561 BinaryOpKind::And => "and",
3562 BinaryOpKind::Or => "or",
3563 BinaryOpKind::Concat => "+",
3564 BinaryOpKind::BitXor => "+",
3565 BinaryOpKind::Shl => "*",
3566 BinaryOpKind::Shr => "/",
3567 };
3568 format!("{} {} {}", l, op_str, r)
3569 }
3570 Expr::Not { operand } => {
3571 let inner = decompile_expr(operand, interner);
3572 format!("not {}", inner)
3573 }
3574 Expr::Call { function, args } => {
3575 let fn_name = interner.resolve(*function);
3576 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3577 if arg_strs.is_empty() {
3578 format!("{}()", fn_name)
3579 } else {
3580 format!("{}({})", fn_name, arg_strs.join(", "))
3581 }
3582 }
3583 Expr::Index { collection, index } => {
3584 let coll = decompile_expr(collection, interner);
3585 let idx = decompile_expr(index, interner);
3586 format!("item {} of {}", idx, coll)
3587 }
3588 Expr::Length { collection } => {
3589 let coll = decompile_expr(collection, interner);
3590 format!("length of {}", coll)
3591 }
3592 Expr::FieldAccess { object, field } => {
3593 let obj = decompile_expr(object, interner);
3594 let field_name = interner.resolve(*field);
3595 format!("{} of {}", field_name, obj)
3596 }
3597 Expr::New { type_name, .. } => {
3598 let tn = interner.resolve(*type_name);
3599 format!("a new {}", tn)
3600 }
3601 Expr::NewVariant { variant, fields, .. } => {
3602 let vn = interner.resolve(*variant);
3603 if fields.is_empty() {
3604 format!("a new {}", vn)
3605 } else {
3606 let parts: Vec<String> = fields.iter().map(|(name, val)| {
3607 let n = interner.resolve(*name);
3608 let v = decompile_expr(val, interner);
3609 format!("{} {}", n, v)
3610 }).collect();
3611 format!("a new {} with {}", vn, parts.join(" and "))
3612 }
3613 }
3614 Expr::InterpolatedString(parts) => {
3615 let mut result = String::new();
3616 for part in parts {
3617 match part {
3618 StringPart::Literal(sym) => {
3619 result.push_str(&interner.resolve(*sym));
3620 }
3621 StringPart::Expr { value, debug, .. } => {
3622 let expr_str = decompile_expr(value, interner);
3623 if *debug {
3624 result.push_str(&format!("{{{}=}}", expr_str));
3625 } else {
3626 result.push_str(&format!("{{{}}}", expr_str));
3627 }
3628 }
3629 }
3630 }
3631 format!("\"{}\"", result)
3632 }
3633 Expr::Slice { collection, start, end } => {
3634 let coll = decompile_expr(collection, interner);
3635 let s = decompile_expr(start, interner);
3636 let e = decompile_expr(end, interner);
3637 format!("{} {} through {}", coll, s, e)
3638 }
3639 Expr::Copy { expr } => {
3640 let inner = decompile_expr(expr, interner);
3641 format!("copy of {}", inner)
3642 }
3643 Expr::Give { value } => {
3644 let inner = decompile_expr(value, interner);
3645 format!("Give {}", inner)
3646 }
3647 Expr::Contains { collection, value } => {
3648 let coll = decompile_expr(collection, interner);
3649 let val = decompile_expr(value, interner);
3650 format!("{} contains {}", coll, val)
3651 }
3652 Expr::Union { left, right } => {
3653 let l = decompile_expr(left, interner);
3654 let r = decompile_expr(right, interner);
3655 format!("{} union {}", l, r)
3656 }
3657 Expr::Intersection { left, right } => {
3658 let l = decompile_expr(left, interner);
3659 let r = decompile_expr(right, interner);
3660 format!("{} intersection {}", l, r)
3661 }
3662 Expr::List(elems) => {
3663 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3664 format!("[{}]", parts.join(", "))
3665 }
3666 Expr::Tuple(elems) => {
3667 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3668 format!("({})", parts.join(", "))
3669 }
3670 Expr::Range { start, end } => {
3671 let s = decompile_expr(start, interner);
3672 let e = decompile_expr(end, interner);
3673 format!("{} to {}", s, e)
3674 }
3675 Expr::OptionSome { value } => {
3676 let inner = decompile_expr(value, interner);
3677 format!("some {}", inner)
3678 }
3679 Expr::OptionNone => "none".to_string(),
3680 Expr::WithCapacity { value, capacity } => {
3681 let val = decompile_expr(value, interner);
3682 let cap = decompile_expr(capacity, interner);
3683 format!("{} with capacity {}", val, cap)
3684 }
3685 Expr::Escape { language, code } => {
3686 let lang = interner.resolve(*language);
3687 let src = interner.resolve(*code);
3688 format!("Escape to {}:\n{}", lang, src)
3689 }
3690 Expr::ManifestOf { zone } => {
3691 let z = decompile_expr(zone, interner);
3692 format!("the manifest of {}", z)
3693 }
3694 Expr::ChunkAt { index, zone } => {
3695 let idx = decompile_expr(index, interner);
3696 let z = decompile_expr(zone, interner);
3697 format!("the chunk at {} in {}", idx, z)
3698 }
3699 Expr::Closure { params, body, return_type } => {
3700 let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
3701 let n = interner.resolve(*name);
3702 let t = decompile_type_expr(ty, interner);
3703 format!("{}: {}", n, t)
3704 }).collect();
3705 let ret = if let Some(rt) = return_type {
3706 format!(" -> {}", decompile_type_expr(rt, interner))
3707 } else {
3708 String::new()
3709 };
3710 match body {
3711 ClosureBody::Expression(expr) => {
3712 let e = decompile_expr(expr, interner);
3713 format!("({}){} -> {}", param_strs.join(", "), ret, e)
3714 }
3715 ClosureBody::Block(_) => {
3716 format!("({}){} -> [block]", param_strs.join(", "), ret)
3717 }
3718 }
3719 }
3720 Expr::CallExpr { callee, args } => {
3721 let c = decompile_expr(callee, interner);
3722 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3723 format!("{}({})", c, arg_strs.join(", "))
3724 }
3725 }
3726}
3727
3728fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
3729 match ty {
3730 TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
3731 TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
3732 TypeExpr::Generic { base, params } => {
3733 let base_str = interner.resolve(*base);
3734 let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
3735 format!("{} of {}", base_str, param_strs.join(" and "))
3736 }
3737 TypeExpr::Function { inputs, output } => {
3738 let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
3739 let out_str = decompile_type_expr(output, interner);
3740 format!("fn({}) -> {}", in_strs.join(", "), out_str)
3741 }
3742 TypeExpr::Refinement { base, .. } => {
3743 decompile_type_expr(base, interner)
3744 }
3745 TypeExpr::Persistent { inner } => {
3746 format!("Persistent {}", decompile_type_expr(inner, interner))
3747 }
3748 }
3749}
3750
3751pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
3758 let mut interner = Interner::new();
3759 let mut lexer = Lexer::new(source, &mut interner);
3760 let tokens = lexer.tokenize();
3761
3762 let type_registry = {
3763 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3764 let result = discovery.run_full();
3765 result.types
3766 };
3767
3768 let mut world_state = WorldState::new();
3769 let expr_arena = Arena::new();
3770 let term_arena = Arena::new();
3771 let np_arena = Arena::new();
3772 let sym_arena = Arena::new();
3773 let role_arena = Arena::new();
3774 let pp_arena = Arena::new();
3775 let stmt_arena: Arena<Stmt> = Arena::new();
3776 let imperative_expr_arena: Arena<Expr> = Arena::new();
3777 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3778
3779 let ast_ctx = AstContext::with_types(
3780 &expr_arena, &term_arena, &np_arena, &sym_arena,
3781 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3782 &type_expr_arena,
3783 );
3784
3785 let mut parser = crate::parser::Parser::new(
3786 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3787 );
3788 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3789
3790 verify_no_overhead_stmts(&stmts, &interner)
3791}
3792
3793const CORE_VARIANT_NAMES: &[&str] = &[
3794 "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
3795 "CCall", "CIndex", "CLen", "CMapGet",
3796 "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
3797 "CCallS", "CPush", "CSetIdx", "CMapSet",
3798 "CFuncDef", "CProg",
3799 "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
3800];
3801
3802fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
3803 for stmt in stmts {
3804 check_stmt_overhead(stmt, interner)?;
3805 }
3806 Ok(())
3807}
3808
3809fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
3810 match stmt {
3811 Stmt::Inspect { arms, .. } => {
3812 for arm in arms {
3813 if let Some(variant) = arm.variant {
3814 let variant_name = interner.resolve(variant);
3815 if CORE_VARIANT_NAMES.contains(&variant_name) {
3816 return Err(format!(
3817 "Interpretive overhead: Inspect dispatches on Core variant '{}'",
3818 variant_name
3819 ));
3820 }
3821 }
3822 for s in arm.body.iter() {
3823 check_stmt_overhead(s, interner)?;
3824 }
3825 }
3826 }
3827 Stmt::If { cond, then_block, else_block } => {
3828 check_expr_overhead(cond, interner)?;
3829 for s in then_block.iter() {
3830 check_stmt_overhead(s, interner)?;
3831 }
3832 if let Some(els) = else_block {
3833 for s in els.iter() {
3834 check_stmt_overhead(s, interner)?;
3835 }
3836 }
3837 }
3838 Stmt::While { cond, body, .. } => {
3839 check_expr_overhead(cond, interner)?;
3840 for s in body.iter() {
3841 check_stmt_overhead(s, interner)?;
3842 }
3843 }
3844 Stmt::FunctionDef { body, .. } => {
3845 for s in body.iter() {
3846 check_stmt_overhead(s, interner)?;
3847 }
3848 }
3849 Stmt::Repeat { body, .. } => {
3850 for s in body.iter() {
3851 check_stmt_overhead(s, interner)?;
3852 }
3853 }
3854 Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
3855 check_expr_overhead(value, interner)?;
3856 }
3857 Stmt::Return { value } => {
3858 if let Some(v) = value {
3859 check_expr_overhead(v, interner)?;
3860 }
3861 }
3862 _ => {}
3863 }
3864 Ok(())
3865}
3866
3867fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
3868 match expr {
3869 Expr::Index { collection, index } => {
3870 if let Expr::Identifier(coll_sym) = collection {
3872 let coll_name = interner.resolve(*coll_sym);
3873 if coll_name == "env" {
3874 if let Expr::Literal(Literal::Text(_)) = index {
3875 return Err(
3876 "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
3877 );
3878 }
3879 }
3880 }
3881 check_expr_overhead(collection, interner)?;
3882 check_expr_overhead(index, interner)?;
3883 }
3884 Expr::New { type_name, .. } => {
3885 let tn = interner.resolve(*type_name);
3886 if CORE_VARIANT_NAMES.contains(&tn) {
3887 return Err(format!(
3888 "Interpretive overhead: Core type constructor 'new {}'", tn
3889 ));
3890 }
3891 }
3892 Expr::NewVariant { variant, .. } => {
3893 let vn = interner.resolve(*variant);
3894 if CORE_VARIANT_NAMES.contains(&vn) {
3895 return Err(format!(
3896 "Interpretive overhead: Core variant constructor '{}'", vn
3897 ));
3898 }
3899 }
3900 Expr::Call { function, args } => {
3901 let fn_name = interner.resolve(*function);
3902 if CORE_VARIANT_NAMES.contains(&fn_name) {
3903 return Err(format!(
3904 "Interpretive overhead: Core variant call '{}'", fn_name
3905 ));
3906 }
3907 for a in args {
3908 check_expr_overhead(a, interner)?;
3909 }
3910 }
3911 Expr::BinaryOp { left, right, .. } => {
3912 check_expr_overhead(left, interner)?;
3913 check_expr_overhead(right, interner)?;
3914 }
3915 Expr::Not { operand } => {
3916 check_expr_overhead(operand, interner)?;
3917 }
3918 Expr::Length { collection } => {
3919 check_expr_overhead(collection, interner)?;
3920 }
3921 Expr::FieldAccess { object, .. } => {
3922 check_expr_overhead(object, interner)?;
3923 }
3924 _ => {}
3925 }
3926 Ok(())
3927}
3928
3929pub fn pe_source_text() -> &'static str {
3935 include_str!("optimize/pe_source.logos")
3936}
3937
3938pub fn decompile_source_text() -> &'static str {
3939 include_str!("optimize/decompile_source.logos")
3940}
3941
3942pub fn pe_bti_source_text() -> &'static str {
3943 include_str!("optimize/pe_bti_source.logos")
3944}
3945
3946pub fn pe_mini_source_text() -> &'static str {
3947 include_str!("optimize/pe_mini_source.logos")
3948}
3949
3950const CORE_TYPES_FOR_PE: &str = r#"
3951## A CExpr is one of:
3952 A CInt with value Int.
3953 A CFloat with value Real.
3954 A CBool with value Bool.
3955 A CText with value Text.
3956 A CVar with name Text.
3957 A CBinOp with op Text and left CExpr and right CExpr.
3958 A CNot with inner CExpr.
3959 A CCall with name Text and args Seq of CExpr.
3960 A CIndex with coll CExpr and idx CExpr.
3961 A CLen with target CExpr.
3962 A CMapGet with target CExpr and key CExpr.
3963 A CNewSeq.
3964 A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
3965 A CList with items Seq of CExpr.
3966 A CRange with start CExpr and end CExpr.
3967 A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
3968 A CCopy with target CExpr.
3969 A CNewSet.
3970 A CContains with coll CExpr and elem CExpr.
3971 A CUnion with left CExpr and right CExpr.
3972 A CIntersection with left CExpr and right CExpr.
3973 A COptionSome with inner CExpr.
3974 A COptionNone.
3975 A CTuple with items Seq of CExpr.
3976 A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
3977 A CFieldAccess with target CExpr and field Text.
3978 A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
3979 A CCallExpr with target CExpr and args Seq of CExpr.
3980 A CInterpolatedString with parts Seq of CStringPart.
3981 A CDuration with amount CExpr and unit Text.
3982 A CTimeNow.
3983 A CDateToday.
3984 A CEscExpr with code Text.
3985
3986## A CStringPart is one of:
3987 A CLiteralPart with value Text.
3988 A CExprPart with expr CExpr.
3989
3990## A CStmt is one of:
3991 A CLet with name Text and expr CExpr.
3992 A CSet with name Text and expr CExpr.
3993 A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
3994 A CWhile with cond CExpr and body Seq of CStmt.
3995 A CReturn with expr CExpr.
3996 A CShow with expr CExpr.
3997 A CCallS with name Text and args Seq of CExpr.
3998 A CPush with expr CExpr and target Text.
3999 A CSetIdx with target Text and idx CExpr and val CExpr.
4000 A CMapSet with target Text and key CExpr and val CExpr.
4001 A CPop with target Text.
4002 A CRepeat with var Text and coll CExpr and body Seq of CStmt.
4003 A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
4004 A CBreak.
4005 A CAdd with elem CExpr and target Text.
4006 A CRemove with elem CExpr and target Text.
4007 A CSetField with target Text and field Text and val CExpr.
4008 A CStructDef with name Text and fieldNames Seq of Text.
4009 A CInspect with target CExpr and arms Seq of CMatchArm.
4010 A CEnumDef with name Text and variants Seq of Text.
4011 A CRuntimeAssert with cond CExpr and msg CExpr.
4012 A CGive with expr CExpr and target Text.
4013 A CEscStmt with code Text.
4014 A CSleep with duration CExpr.
4015 A CReadConsole with target Text.
4016 A CReadFile with path CExpr and target Text.
4017 A CWriteFile with path CExpr and content CExpr.
4018 A CCheck with predicate CExpr and msg CExpr.
4019 A CAssert with proposition CExpr.
4020 A CTrust with proposition CExpr and justification Text.
4021 A CRequire with dependency Text.
4022 A CMerge with target Text and other CExpr.
4023 A CIncrease with target Text and amount CExpr.
4024 A CDecrease with target Text and amount CExpr.
4025 A CAppendToSeq with target Text and value CExpr.
4026 A CResolve with target Text.
4027 A CSync with target Text and channel CExpr.
4028 A CMount with target Text and path CExpr.
4029 A CConcurrent with branches Seq of Seq of CStmt.
4030 A CParallel with branches Seq of Seq of CStmt.
4031 A CLaunchTask with body Seq of CStmt and handle Text.
4032 A CStopTask with handle CExpr.
4033 A CSelect with branches Seq of CSelectBranch.
4034 A CCreatePipe with name Text and capacity CExpr.
4035 A CSendPipe with chan Text and value CExpr.
4036 A CReceivePipe with chan Text and target Text.
4037 A CTrySendPipe with chan Text and value CExpr.
4038 A CTryReceivePipe with chan Text and target Text.
4039 A CSpawn with agentType Text and target Text.
4040 A CSendMessage with target CExpr and msg CExpr.
4041 A CAwaitMessage with target Text.
4042 A CListen with addr CExpr and handler Text.
4043 A CConnectTo with addr CExpr and target Text.
4044 A CZone with name Text and kind Text and body Seq of CStmt.
4045
4046## A CSelectBranch is one of:
4047 A CSelectRecv with chan Text and var Text and body Seq of CStmt.
4048 A CSelectTimeout with duration CExpr and body Seq of CStmt.
4049
4050## A CMatchArm is one of:
4051 A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
4052 A COtherwise with body Seq of CStmt.
4053
4054## A CFunc is one of:
4055 A CFuncDef with name Text and params Seq of Text and body Seq of CStmt.
4056
4057## A CProgram is one of:
4058 A CProg with funcs Seq of CFunc and main Seq of CStmt.
4059
4060## A PEState is one of:
4061 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.
4062
4063## A CVal is one of:
4064 A VInt with value Int.
4065 A VFloat with value Real.
4066 A VBool with value Bool.
4067 A VText with value Text.
4068 A VSeq with items Seq of CVal.
4069 A VMap with entries Map of Text to CVal.
4070 A VError with msg Text.
4071 A VNothing.
4072 A VSet with items Seq of CVal.
4073 A VOption with inner CVal and present Bool.
4074 A VTuple with items Seq of CVal.
4075 A VStruct with typeName Text and fields Map of Text to CVal.
4076 A VVariant with typeName Text and variantName Text and fields Seq of CVal.
4077 A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
4078 A VDuration with millis Int.
4079 A VDate with year Int and month Int and day Int.
4080 A VMoment with millis Int.
4081 A VSpan with startMillis Int and endMillis Int.
4082 A VTime with hour Int and minute Int and second Int.
4083 A VCrdt with kind Text and state Map of Text to CVal.
4084"#;
4085
4086pub fn quote_pe_source() -> Result<String, String> {
4094 let pe_source = pe_source_text();
4095 let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
4096 let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
4097 Ok(format!("{}\n{}", pe_source, encoded))
4098}
4099
4100pub fn projection2_source() -> Result<String, String> {
4114 let pe_source = pe_source_text();
4115
4116 let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
4117
4118 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
4119}
4120
4121pub fn projection3_source() -> Result<String, String> {
4136 let pe_source = pe_source_text();
4137
4138 let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
4139
4140 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
4141}
4142
4143pub fn run_logos_source(source: &str) -> Result<String, String> {
4148 let compile_output = compile_program_full(source)
4149 .map_err(|e| format!("Compilation failed: {:?}", e))?;
4150
4151 let temp_base = std::env::temp_dir().join("logos_run_source");
4153 std::fs::create_dir_all(&temp_base)
4154 .map_err(|e| format!("mkdir failed: {}", e))?;
4155
4156 let pkg_name = format!(
4157 "logos_run_{}_{}",
4158 std::process::id(),
4159 std::time::SystemTime::now()
4160 .duration_since(std::time::UNIX_EPOCH)
4161 .unwrap()
4162 .as_nanos()
4163 );
4164 let project_dir = temp_base.join(&pkg_name);
4165
4166 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
4168 let workspace_root = manifest_dir.parent().unwrap().parent().unwrap();
4169
4170 let cargo_toml = format!(
4171 r#"[package]
4172name = "{}"
4173version = "0.1.0"
4174edition = "2021"
4175
4176[dependencies]
4177logicaffeine-data = {{ path = "{}/crates/logicaffeine_data" }}
4178logicaffeine-system = {{ path = "{}/crates/logicaffeine_system", features = ["full"] }}
4179tokio = {{ version = "1", features = ["rt-multi-thread", "macros"] }}
4180serde = {{ version = "1", features = ["derive"] }}
4181rayon = "1"
4182"#,
4183 pkg_name,
4184 workspace_root.display(),
4185 workspace_root.display(),
4186 );
4187
4188 std::fs::create_dir_all(project_dir.join("src"))
4189 .map_err(|e| format!("mkdir failed: {}", e))?;
4190 std::fs::write(project_dir.join("Cargo.toml"), cargo_toml)
4191 .map_err(|e| format!("Write Cargo.toml failed: {}", e))?;
4192 std::fs::write(project_dir.join("src/main.rs"), &compile_output.rust_code)
4193 .map_err(|e| format!("Write main.rs failed: {}", e))?;
4194
4195 let target_dir = std::env::temp_dir().join("logos_e2e_cache");
4197 std::fs::create_dir_all(&target_dir)
4198 .map_err(|e| format!("mkdir target failed: {}", e))?;
4199
4200 let output = std::process::Command::new("cargo")
4201 .args(["run", "--quiet"])
4202 .current_dir(&project_dir)
4203 .env("CARGO_TARGET_DIR", &target_dir)
4204 .env("RUST_MIN_STACK", "268435456")
4205 .output()
4206 .map_err(|e| format!("cargo run failed: {}", e))?;
4207
4208 let _ = std::fs::remove_dir_all(&project_dir);
4210
4211 if !output.status.success() {
4212 return Err(format!(
4213 "Execution failed:\nstderr: {}\nstdout: {}",
4214 String::from_utf8_lossy(&output.stderr),
4215 String::from_utf8_lossy(&output.stdout),
4216 ));
4217 }
4218
4219 Ok(String::from_utf8_lossy(&output.stdout).to_string())
4220}
4221
4222pub struct GenuineProjectionResult {
4225 pub source: String,
4228 pub block_entry: String,
4231 pub expr_entry: Option<String>,
4233}
4234
4235fn discover_entry_points(residual: &str, block_prefix: &str, expr_prefix: &str)
4238 -> (String, Option<String>)
4239{
4240 let mut block_entry = String::new();
4241 let mut expr_entry = None;
4242 for line in residual.lines() {
4243 let trimmed = line.trim();
4244 if let Some(rest) = trimmed.strip_prefix("## To ") {
4245 let name = rest.split(" (").next().unwrap_or("").trim();
4247 if name.starts_with(block_prefix) && block_entry.is_empty() {
4248 block_entry = name.to_string();
4249 } else if name.starts_with(expr_prefix) && expr_entry.is_none() {
4250 expr_entry = Some(name.to_string());
4251 }
4252 }
4253 }
4254 (block_entry, expr_entry)
4255}
4256
4257pub fn projection1_source_real(core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
4262 let full_source = if program.contains("## Main") || program.contains("## To ") {
4263 program.to_string()
4264 } else {
4265 format!("## Main\n{}", program)
4266 };
4267
4268 let encoded = encode_program_source(&full_source)
4270 .map_err(|e| format!("Failed to encode program: {:?}", e))?;
4271
4272 let pe_source = pe_source_text();
4274 let decompile_source = decompile_source_text();
4275
4276 let actual_core_types = if core_types.is_empty() { CORE_TYPES_FOR_PE } else { core_types };
4278
4279 let driver = r#"
4280 Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4281 Let residual be peBlock(encodedMain, state).
4282 Let source be decompileBlock(residual, 0).
4283 Show source.
4284"#;
4285
4286 let combined = format!(
4287 "{}\n{}\n{}\n## Main\n{}\n{}",
4288 actual_core_types,
4289 pe_source,
4290 decompile_source,
4291 encoded,
4292 driver,
4293 );
4294
4295 let raw_residual = run_logos_source(&combined)?;
4297 let trimmed = raw_residual.trim();
4298
4299 if trimmed.is_empty() {
4301 return Ok("## Main\n".to_string());
4302 }
4303
4304 if trimmed.contains("## To ") {
4306 Ok(trimmed.to_string())
4307 } else {
4308 Ok(format!("## Main\n{}", trimmed))
4309 }
4310}
4311
4312pub fn run_genuine_p2_on_target(program: &str, core_types: &str, interpreter: &str) -> Result<String, String> {
4324 let pe_mini = pe_mini_source_text();
4325 let pe = pe_source_text();
4326
4327 let full_source = if program.contains("## Main") || program.contains("## To ") {
4328 program.to_string()
4329 } else {
4330 format!("## Main\n{}", program)
4331 };
4332
4333 let target_encoded = encode_program_source(&full_source)
4338 .map_err(|e| format!("Failed to encode target: {:?}", e))?;
4339 let pe_mini_prog = format!(
4340 "{}\n{}\n{}\n## Main\n{}\n\
4341 Let compileEnv be a new Map of Text to CVal.\n\
4342 Let compileState be makePeState(compileEnv, encodedFuncMap, 200).\n\
4343 Let compiled be peBlockM(encodedMain, compileState).\n\
4344 Let runEnv be a new Map of Text to CVal.\n\
4345 coreExecBlock(compiled, runEnv, encodedFuncMap).\n",
4346 core_types, pe_mini, interpreter, target_encoded
4347 );
4348
4349 let encoded = encode_program_source_compact(&pe_mini_prog)
4351 .map_err(|e| format!("Failed to encode pe_mini+target for P2: {:?}", e))?;
4352
4353 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 500).
4358 Let residual be peBlock(encodedMain, state).
4359 Let allFuncs be peFuncs(state).
4360 Let runEnv be a new Map of Text to CVal.
4361 coreExecBlock(residual, runEnv, allFuncs).
4362"#;
4363 let combined = format!(
4364 "{}\n{}\n{}\n## Main\n{}\n{}",
4365 CORE_TYPES_FOR_PE, pe, interpreter, encoded, driver
4366 );
4367
4368 run_logos_source(&combined)
4369}
4370
4371pub fn run_genuine_p3_on_target(program: &str, core_types: &str, interpreter: &str) -> Result<String, String> {
4373 let pe_bti = pe_bti_source_text();
4374 let pe = pe_source_text();
4375
4376 let full_source = if program.contains("## Main") || program.contains("## To ") {
4377 program.to_string()
4378 } else {
4379 format!("## Main\n{}", program)
4380 };
4381
4382 let bti_types = CORE_TYPES_FOR_PE
4383 .replace("specResults", "memoCache")
4384 .replace("onStack", "callGuard");
4385
4386 let target_encoded = encode_program_source(&full_source)
4387 .map_err(|e| format!("Failed to encode target: {:?}", e))?;
4388 let pe_bti_prog = format!(
4389 "{}\n{}\n{}\n## Main\n{}\n\
4390 Let compileEnv be a new Map of Text to CVal.\n\
4391 Let compileState be makePeState(compileEnv, encodedFuncMap, 200).\n\
4392 Let compiled be peBlockB(encodedMain, compileState).\n\
4393 Let runEnv be a new Map of Text to CVal.\n\
4394 coreExecBlock(compiled, runEnv, encodedFuncMap).\n",
4395 bti_types, pe_bti, interpreter, target_encoded
4396 );
4397
4398 let encoded = encode_program_source_compact(&pe_bti_prog)
4399 .map_err(|e| format!("Failed to encode pe_bti+target for P3: {:?}", e))?;
4400
4401 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4403 Let residual be peBlock(encodedMain, state).
4404 Let runEnv be a new Map of Text to CVal.
4405 coreExecBlock(residual, runEnv, encodedFuncMap).
4406"#;
4407 let combined = format!(
4408 "{}\n{}\n{}\n## Main\n{}\n{}",
4409 CORE_TYPES_FOR_PE, pe, interpreter, encoded, driver
4410 );
4411
4412 run_logos_source(&combined)
4413}
4414
4415pub fn projection2_source_real(_core_types: &str, _interpreter: &str) -> Result<GenuineProjectionResult, String> {
4425 let pe_mini = pe_mini_source_text();
4426 let pe = pe_source_text();
4427 let decompile = decompile_source_text();
4428
4429 let program = format!(
4431 "{}\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 peExprM(targetExpr, state).\n Inspect result:\n When CInt (v):\n Show v.\n Otherwise:\n Show \"dynamic\".\n",
4432 CORE_TYPES_FOR_PE, pe_mini
4433 );
4434
4435 let encoded = encode_program_source_compact(&program)
4437 .map_err(|e| format!("Failed to encode pe_mini for P2: {:?}", e))?;
4438
4439 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4443 Let residual be peBlock(encodedMain, state).
4444 Let nl be chr(10).
4445 Let mutable output be "".
4446 Let specFuncs be peFuncs(state).
4447 Let mutable allNames be collectCallNames(residual).
4448 Let mutable emitted be a new Map of Text to Bool.
4449 Let mutable changed be true.
4450 While changed:
4451 Set changed to false.
4452 Let mutable toAdd be a new Seq of Text.
4453 Repeat for fnKey in allNames:
4454 Let fkStr be "{fnKey}".
4455 If emitted contains fkStr:
4456 Let skipE be true.
4457 Otherwise:
4458 Set item fkStr of emitted to true.
4459 Let fkStr2 be "{fnKey}".
4460 If specFuncs contains fkStr2:
4461 Let fdef be item fkStr2 of specFuncs.
4462 Inspect fdef:
4463 When CFuncDef (fn0, ps0, body0):
4464 Let children be collectCallNames(body0).
4465 Repeat for child in children:
4466 Let childStr be "{child}".
4467 If not emitted contains childStr:
4468 Push child to toAdd.
4469 Set changed to true.
4470 Otherwise:
4471 Let skipF be true.
4472 Repeat for ta in toAdd:
4473 Push ta to allNames.
4474 Repeat for fnKey in allNames:
4475 Let fkStr be "{fnKey}".
4476 If specFuncs contains fkStr:
4477 Let fdef be item fkStr of specFuncs.
4478 Let funcSrc be decompileFunc(fdef).
4479 If the length of funcSrc is greater than 0:
4480 Set output to "{output}{funcSrc}{nl}".
4481 Let mainSrc be decompileBlock(residual, 0).
4482 Set output to "{output}## Main{nl}{mainSrc}".
4483 Show output.
4484"#;
4485 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4486
4487 let result = run_logos_source(&combined)?;
4488
4489 let result = fix_decompiled_types(&result, &[
4492 ("peExprM_", "(e: CExpr) -> CExpr:"),
4493 ("peBlockM_", "(stmts: Seq of CStmt) -> Seq of CStmt:"),
4494 ("checkLiteralM_", "(e: CExpr) -> Bool:"),
4495 ("exprToValM_", "(e: CExpr) -> CVal:"),
4496 ("valToExprM_", "(v: CVal) -> CExpr:"),
4497 ("evalBinOpM_", "(binOp: Text) and (lv: CVal) and (rv: CVal) -> CVal:"),
4498 ("isCopyPropSafeM_", "(e: CExpr) -> Bool:"),
4499 ("checkVNothingM_", "(v: CVal) -> Bool:"),
4500 ("hasReturnM_", "(stmts: Seq of CStmt) -> Bool:"),
4501 ("extractReturnM_", "(stmts: Seq of CStmt) -> CExpr:"),
4502 ("validateExtractReturnM_", "(result: CExpr) and (bodyStmts: Seq of CStmt) -> CExpr:"),
4503 ("makeKeyM_", "(fnName: Text) and (args: Seq of CExpr) -> Text:"),
4504 ("exprToKeyPartM_", "(e: CExpr) -> Text:"),
4505 ("collectSetVarsM_", "(stmts: Seq of CStmt) -> Seq of Text:"),
4506 ("peEnvM_", "(st: PEMiniState) -> Map of Text to CVal:"),
4507 ("peFuncsM_", "(st: PEMiniState) -> Map of Text to CFunc:"),
4508 ("peDepthM_", "(st: PEMiniState) -> Int:"),
4509 ("peStaticEnvM_", "(st: PEMiniState) -> Map of Text to CExpr:"),
4510 ("peMemoCacheM_", "(st: PEMiniState) -> Map of Text to CExpr:"),
4511 ("peStateWithEnvDepthM_", "(st: PEMiniState) and (newEnv: Map of Text to CVal) and (d: Int) -> PEMiniState:"),
4512 ("peStateWithEnvDepthStaticM_", "(st: PEMiniState) and (newEnv: Map of Text to CVal) and (d: Int) and (newSe: Map of Text to CExpr) -> PEMiniState:"),
4513 ]);
4514
4515 let (_block_entry, expr_entry) = discover_entry_points(&result, "peBlockM_", "peExprM_");
4516 let expr_fn = expr_entry.as_ref()
4517 .ok_or_else(|| "Genuine P2: no peExprM_ entry found in residual".to_string())?;
4518
4519 let func_defs_only = strip_main_block(&result);
4522
4523 let pe_mini_helpers = pe_mini_source_text();
4527
4528 let wrapper = generate_block_wrapper(expr_fn, "compileBlock");
4530
4531 let combined = format!("{}\n{}\n{}", pe_mini_helpers, func_defs_only, wrapper);
4535 let full_source = deduplicate_functions(&combined);
4536
4537 Ok(GenuineProjectionResult {
4538 source: full_source,
4539 block_entry: "compileBlock".to_string(),
4540 expr_entry,
4541 })
4542}
4543
4544pub fn genuine_projection2_residual() -> Result<String, String> {
4555 let pe_mini = pe_mini_source_text();
4556 let pe = pe_source_text();
4557 let decompile = decompile_source_text();
4558
4559 let program = format!(
4561 "{}\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 peExprM(targetExpr, state).\n Inspect result:\n When CInt (v):\n Show v.\n Otherwise:\n Show \"dynamic\".\n",
4562 CORE_TYPES_FOR_PE, pe_mini
4563 );
4564
4565 let encoded = encode_program_source_compact(&program)
4567 .map_err(|e| format!("Failed to encode pe_mini: {:?}", e))?;
4568
4569 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4571 Let residual be peBlock(encodedMain, state).
4572 Let nl be chr(10).
4573 Let mutable output be "".
4574 Let specFuncs be peFuncs(state).
4575 Let specNames be collectCallNames(residual).
4576 Repeat for sn in specNames:
4577 Let snKey be "{sn}".
4578 If specFuncs contains snKey:
4579 Let fdef be item snKey of specFuncs.
4580 Let funcSrc be decompileFunc(fdef).
4581 If the length of funcSrc is greater than 0:
4582 Set output to "{output}{funcSrc}{nl}".
4583 Let mainSrc be decompileBlock(residual, 0).
4584 Set output to "{output}## Main{nl}{mainSrc}".
4585 Show output.
4586"#;
4587 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4588
4589 let result = run_logos_source(&combined)?;
4590 Ok(result)
4591}
4592
4593pub fn genuine_projection3_residual() -> Result<String, String> {
4604 let pe_bti = pe_bti_source_text();
4605 let pe = pe_source_text();
4606 let decompile = decompile_source_text();
4607
4608 let bti_types = CORE_TYPES_FOR_PE
4610 .replace("specResults", "memoCache")
4611 .replace("onStack", "callGuard");
4612
4613 let program = format!(
4615 "{}\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 peExprB(targetExpr, state).\n Inspect result:\n When CInt (v):\n Show v.\n Otherwise:\n Show \"dynamic\".\n",
4616 bti_types, pe_bti
4617 );
4618
4619 let encoded = encode_program_source_compact(&program)
4621 .map_err(|e| format!("Failed to encode pe_bti: {:?}", e))?;
4622
4623 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4625 Let residual be peBlock(encodedMain, state).
4626 Let nl be chr(10).
4627 Let mutable output be "".
4628 Let specFuncs be peFuncs(state).
4629 Let specNames be collectCallNames(residual).
4630 Repeat for sn in specNames:
4631 Let snKey be "{sn}".
4632 If specFuncs contains snKey:
4633 Let fdef be item snKey of specFuncs.
4634 Let funcSrc be decompileFunc(fdef).
4635 If the length of funcSrc is greater than 0:
4636 Set output to "{output}{funcSrc}{nl}".
4637 Let mainSrc be decompileBlock(residual, 0).
4638 Set output to "{output}## Main{nl}{mainSrc}".
4639 Show output.
4640"#;
4641 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4642
4643 let result = run_logos_source(&combined)?;
4644 Ok(result)
4645}
4646
4647pub fn projection3_source_real(_core_types: &str) -> Result<GenuineProjectionResult, String> {
4656 let pe_bti = pe_bti_source_text();
4657 let pe = pe_source_text();
4658 let decompile = decompile_source_text();
4659
4660 let bti_types = CORE_TYPES_FOR_PE
4662 .replace("specResults", "memoCache")
4663 .replace("onStack", "callGuard");
4664
4665 let program = format!(
4667 "{}\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 peExprB(targetExpr, state).\n Inspect result:\n When CInt (v):\n Show v.\n Otherwise:\n Show \"dynamic\".\n",
4668 bti_types, pe_bti
4669 );
4670
4671 let encoded = encode_program_source_compact(&program)
4673 .map_err(|e| format!("Failed to encode pe_bti for P3: {:?}", e))?;
4674
4675 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4677 Let residual be peBlock(encodedMain, state).
4678 Let nl be chr(10).
4679 Let mutable output be "".
4680 Let specFuncs be peFuncs(state).
4681 Let mutable allNames be collectCallNames(residual).
4682 Let mutable emitted be a new Map of Text to Bool.
4683 Let mutable changed be true.
4684 While changed:
4685 Set changed to false.
4686 Let mutable toAdd be a new Seq of Text.
4687 Repeat for fnKey in allNames:
4688 Let fkStr be "{fnKey}".
4689 If emitted contains fkStr:
4690 Let skipE be true.
4691 Otherwise:
4692 Set item fkStr of emitted to true.
4693 Let fkStr2 be "{fnKey}".
4694 If specFuncs contains fkStr2:
4695 Let fdef be item fkStr2 of specFuncs.
4696 Inspect fdef:
4697 When CFuncDef (fn0, ps0, body0):
4698 Let children be collectCallNames(body0).
4699 Repeat for child in children:
4700 Let childStr be "{child}".
4701 If not emitted contains childStr:
4702 Push child to toAdd.
4703 Set changed to true.
4704 Otherwise:
4705 Let skipF be true.
4706 Repeat for ta in toAdd:
4707 Push ta to allNames.
4708 Repeat for fnKey in allNames:
4709 Let fkStr be "{fnKey}".
4710 If specFuncs contains fkStr:
4711 Let fdef be item fkStr of specFuncs.
4712 Let funcSrc be decompileFunc(fdef).
4713 If the length of funcSrc is greater than 0:
4714 Set output to "{output}{funcSrc}{nl}".
4715 Let mainSrc be decompileBlock(residual, 0).
4716 Set output to "{output}## Main{nl}{mainSrc}".
4717 Show output.
4718"#;
4719 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4720
4721 let result = run_logos_source(&combined)?;
4722
4723 let result = fix_decompiled_types(&result, &[
4725 ("peExprB_", "(e: CExpr) -> CExpr:"),
4726 ("peBlockB_", "(stmts: Seq of CStmt) -> Seq of CStmt:"),
4727 ("isStatic_", "(e: CExpr) -> Bool:"),
4728 ("isLiteral_", "(e: CExpr) -> Bool:"),
4729 ("allStatic_", "(args: Seq of CExpr) -> Bool:"),
4730 ("exprToVal_", "(e: CExpr) -> CVal:"),
4731 ("valToExpr_", "(v: CVal) -> CExpr:"),
4732 ("evalBinOp_", "(binOp: Text) and (lv: CVal) and (rv: CVal) -> CVal:"),
4733 ("isCopyPropSafe_", "(e: CExpr) -> Bool:"),
4734 ("isVNothing_", "(v: CVal) -> Bool:"),
4735 ("hasReturn_", "(stmts: Seq of CStmt) -> Bool:"),
4736 ("extractReturnB_", "(stmts: Seq of CStmt) -> CExpr:"),
4737 ("makeKey_", "(fnName: Text) and (args: Seq of CExpr) -> Text:"),
4738 ("exprToKeyPartB_", "(e: CExpr) -> Text:"),
4739 ("collectSetVars_", "(stmts: Seq of CStmt) -> Seq of Text:"),
4740 ]);
4741
4742 let (_block_entry, expr_entry) = discover_entry_points(&result, "peBlockB_", "peExprB_");
4743 let expr_fn = expr_entry.as_ref()
4744 .ok_or_else(|| "Genuine P3: no peExprB_ entry found in residual".to_string())?;
4745
4746 let func_defs_only = strip_main_block(&result);
4748
4749 let pe_bti_helpers = pe_bti_source_text();
4751
4752 let wrapper = generate_block_wrapper(expr_fn, "cogenBlock");
4754 let combined = format!("{}\n{}\n{}", pe_bti_helpers, func_defs_only, wrapper);
4755 let full_source = deduplicate_functions(&combined);
4756
4757 Ok(GenuineProjectionResult {
4758 source: full_source,
4759 block_entry: "cogenBlock".to_string(),
4760 expr_entry,
4761 })
4762}
4763
4764fn deduplicate_functions(source: &str) -> String {
4766 let mut seen = std::collections::HashSet::new();
4767 let mut result = String::with_capacity(source.len());
4768 let mut skip_until_next = false;
4769 for line in source.lines() {
4770 let trimmed = line.trim();
4771 if let Some(rest) = trimmed.strip_prefix("## To ") {
4772 let name = rest.split(' ').next().unwrap_or("");
4773 if !seen.insert(name.to_string()) {
4774 skip_until_next = true;
4775 continue;
4776 }
4777 skip_until_next = false;
4778 } else if trimmed.starts_with("## Main") {
4779 skip_until_next = false;
4780 } else if skip_until_next {
4781 if !trimmed.starts_with("## ") {
4783 continue;
4784 }
4785 skip_until_next = false;
4786 }
4787 result.push_str(line);
4788 result.push('\n');
4789 }
4790 result
4791}
4792
4793fn strip_main_block(source: &str) -> String {
4797 let mut result = String::with_capacity(source.len());
4798 let mut in_main = false;
4799 for line in source.lines() {
4800 let trimmed = line.trim();
4801 if trimmed == "## Main" {
4802 in_main = true;
4803 continue;
4804 }
4805 if in_main {
4806 if trimmed.starts_with("## To ") {
4808 in_main = false;
4809 } else {
4810 continue;
4811 }
4812 }
4813 result.push_str(line);
4814 result.push('\n');
4815 }
4816 result
4817}
4818
4819fn extract_main_block(source: &str) -> String {
4821 let mut result = String::new();
4822 let mut in_main = false;
4823 for line in source.lines() {
4824 let trimmed = line.trim();
4825 if trimmed == "## Main" {
4826 in_main = true;
4827 continue;
4828 }
4829 if in_main {
4830 if trimmed.starts_with("## To ") {
4831 break;
4832 }
4833 result.push_str(line);
4834 result.push('\n');
4835 }
4836 }
4837 result
4838}
4839
4840fn fix_decompiled_types(source: &str, type_map: &[(&str, &str)]) -> String {
4844 let mut result = String::with_capacity(source.len());
4846 for line in source.lines() {
4847 let trimmed = line.trim();
4848 if let Some(rest) = trimmed.strip_prefix("## To ") {
4849 let name = rest.split(' ').next().unwrap_or("");
4850 let mut fixed = false;
4851 for (prefix, sig) in type_map {
4852 if name.starts_with(prefix) {
4853 result.push_str(&format!("## To {} {}\n", name, sig));
4854 fixed = true;
4855 break;
4856 }
4857 }
4858 if !fixed {
4859 result.push_str(line);
4860 result.push('\n');
4861 }
4862 } else {
4863 result.push_str(line);
4864 result.push('\n');
4865 }
4866 }
4867 let result = result
4871 .replace("Seq of Any", "Seq of CExpr")
4872 .replace("Set of Any", "Set of CExpr")
4873 .replace(": Any)", ": CExpr)")
4874 .replace("-> Any:", "-> CExpr:");
4875
4876 result
4877}
4878
4879fn generate_block_wrapper(expr_entry: &str, wrapper_name: &str) -> String {
4885 format!(r#"
4886## To {wrapper_name} (stmts: Seq of CStmt) -> Seq of CStmt:
4887 Let result be a new Seq of CStmt.
4888 Repeat for s in stmts:
4889 Inspect s:
4890 When CLet (name, expr):
4891 Push (a new CLet with name name and expr {expr_entry}(expr)) to result.
4892 When CSet (name, expr):
4893 Push (a new CSet with name name and expr {expr_entry}(expr)) to result.
4894 When CIf (cond, thenBlock, elseBlock):
4895 Push (a new CIf with cond {expr_entry}(cond) and thenBlock {wrapper_name}(thenBlock) and elseBlock {wrapper_name}(elseBlock)) to result.
4896 When CWhile (cond, body):
4897 Push (a new CWhile with cond {expr_entry}(cond) and body {wrapper_name}(body)) to result.
4898 When CReturn (expr):
4899 Push (a new CReturn with expr {expr_entry}(expr)) to result.
4900 When CShow (expr):
4901 Push (a new CShow with expr {expr_entry}(expr)) to result.
4902 When CCallS (name, args):
4903 Let newArgs be a new Seq of CExpr.
4904 Repeat for a in args:
4905 Push {expr_entry}(a) to newArgs.
4906 Push (a new CCallS with name name and args newArgs) to result.
4907 When CPush (expr, target):
4908 Push (a new CPush with expr {expr_entry}(expr) and target target) to result.
4909 When CSetIdx (target, idx, val):
4910 Push (a new CSetIdx with target target and idx {expr_entry}(idx) and val {expr_entry}(val)) to result.
4911 When CMapSet (target, key, val):
4912 Push (a new CMapSet with target target and key {expr_entry}(key) and val {expr_entry}(val)) to result.
4913 When CPop (target):
4914 Push (a new CPop with target target) to result.
4915 When CRepeat (repVar, coll, body):
4916 Push (a new CRepeat with var repVar and coll {expr_entry}(coll) and body {wrapper_name}(body)) to result.
4917 When CRepeatRange (rrVar, start, end, body):
4918 Push (a new CRepeatRange with var rrVar and start {expr_entry}(start) and end {expr_entry}(end) and body {wrapper_name}(body)) to result.
4919 When CBreak:
4920 Push a new CBreak to result.
4921 When CAdd (elem, target):
4922 Push (a new CAdd with elem {expr_entry}(elem) and target target) to result.
4923 When CRemove (elem, target):
4924 Push (a new CRemove with elem {expr_entry}(elem) and target target) to result.
4925 When CSetField (target, field, val):
4926 Push (a new CSetField with target target and field field and val {expr_entry}(val)) to result.
4927 When CStructDef (sdName, sdFields):
4928 Push (a new CStructDef with name sdName and fieldNames sdFields) to result.
4929 When CInspect (target, arms):
4930 Let newArms be a new Seq of CMatchArm.
4931 Repeat for arm in arms:
4932 Inspect arm:
4933 When CWhen (vn, bindings, body):
4934 Push (a new CWhen with variantName vn and bindings bindings and body {wrapper_name}(body)) to newArms.
4935 When COtherwise (body):
4936 Push (a new COtherwise with body {wrapper_name}(body)) to newArms.
4937 Push (a new CInspect with target {expr_entry}(target) and arms newArms) to result.
4938 When CEnumDef (edName, edVariants):
4939 Push (a new CEnumDef with name edName and variants edVariants) to result.
4940 When CRuntimeAssert (raCond, raMsg):
4941 Push (a new CRuntimeAssert with cond {expr_entry}(raCond) and msg {expr_entry}(raMsg)) to result.
4942 When CGive (giveExpr, giveTarget):
4943 Push (a new CGive with expr {expr_entry}(giveExpr) and target giveTarget) to result.
4944 When CEscStmt (escCode):
4945 Push (a new CEscStmt with code escCode) to result.
4946 When CSleep (dur):
4947 Push (a new CSleep with duration {expr_entry}(dur)) to result.
4948 When CReadConsole (rcTarget):
4949 Push (a new CReadConsole with target rcTarget) to result.
4950 When CReadFile (rfPath, rfTarget):
4951 Push (a new CReadFile with path {expr_entry}(rfPath) and target rfTarget) to result.
4952 When CWriteFile (wfPath, wfContent):
4953 Push (a new CWriteFile with path {expr_entry}(wfPath) and content {expr_entry}(wfContent)) to result.
4954 When CCheck (chkPred, chkMsg):
4955 Push (a new CCheck with predicate {expr_entry}(chkPred) and msg {expr_entry}(chkMsg)) to result.
4956 When CAssert (assertProp):
4957 Push (a new CAssert with proposition {expr_entry}(assertProp)) to result.
4958 When CTrust (trustProp, trustJust):
4959 Push (a new CTrust with proposition {expr_entry}(trustProp) and justification trustJust) to result.
4960 When CRequire (reqDep):
4961 Push (a new CRequire with dependency reqDep) to result.
4962 When CMerge (mergeTarget, mergeOther):
4963 Push (a new CMerge with target mergeTarget and other {expr_entry}(mergeOther)) to result.
4964 When CIncrease (incTarget, incAmount):
4965 Push (a new CIncrease with target incTarget and amount {expr_entry}(incAmount)) to result.
4966 When CDecrease (decTarget, decAmount):
4967 Push (a new CDecrease with target decTarget and amount {expr_entry}(decAmount)) to result.
4968 When CAppendToSeq (asTarget, asValue):
4969 Push (a new CAppendToSeq with target asTarget and value {expr_entry}(asValue)) to result.
4970 When CResolve (resTarget):
4971 Push (a new CResolve with target resTarget) to result.
4972 When CSync (syncTarget, syncChannel):
4973 Push (a new CSync with target syncTarget and channel {expr_entry}(syncChannel)) to result.
4974 When CMount (mountTarget, mountPath):
4975 Push (a new CMount with target mountTarget and path {expr_entry}(mountPath)) to result.
4976 When CConcurrent (concBranches):
4977 Let newBranches be a new Seq of Seq of CStmt.
4978 Repeat for branch in concBranches:
4979 Push {wrapper_name}(branch) to newBranches.
4980 Push (a new CConcurrent with branches newBranches) to result.
4981 When CParallel (parBranches):
4982 Let newBranches be a new Seq of Seq of CStmt.
4983 Repeat for branch in parBranches:
4984 Push {wrapper_name}(branch) to newBranches.
4985 Push (a new CParallel with branches newBranches) to result.
4986 When CLaunchTask (ltBody, ltHandle):
4987 Push (a new CLaunchTask with body {wrapper_name}(ltBody) and handle ltHandle) to result.
4988 When CStopTask (stHandle):
4989 Push (a new CStopTask with handle {expr_entry}(stHandle)) to result.
4990 When CSelect (selBranches):
4991 Let newBranches be a new Seq of CSelectBranch.
4992 Repeat for br in selBranches:
4993 Inspect br:
4994 When CSelectRecv (chan, bvar, body):
4995 Push (a new CSelectRecv with chan chan and var bvar and body {wrapper_name}(body)) to newBranches.
4996 When CSelectTimeout (dur, body):
4997 Push (a new CSelectTimeout with duration {expr_entry}(dur) and body {wrapper_name}(body)) to newBranches.
4998 Push (a new CSelect with branches newBranches) to result.
4999 When CCreatePipe (cpName, cpCapacity):
5000 Push (a new CCreatePipe with name cpName and capacity {expr_entry}(cpCapacity)) to result.
5001 When CSendPipe (spPipe, spValue):
5002 Push (a new CSendPipe with chan spPipe and value {expr_entry}(spValue)) to result.
5003 When CReceivePipe (rpPipe, rpTarget):
5004 Push (a new CReceivePipe with chan rpPipe and target rpTarget) to result.
5005 When CTrySendPipe (tspPipe, tspValue):
5006 Push (a new CTrySendPipe with chan tspPipe and value {expr_entry}(tspValue)) to result.
5007 When CTryReceivePipe (trpPipe, trpTarget):
5008 Push (a new CTryReceivePipe with chan trpPipe and target trpTarget) to result.
5009 When CSpawn (spawnType, spawnTarget):
5010 Push (a new CSpawn with agentType spawnType and target spawnTarget) to result.
5011 When CSendMessage (smTarget, smMsg):
5012 Push (a new CSendMessage with target {expr_entry}(smTarget) and msg {expr_entry}(smMsg)) to result.
5013 When CAwaitMessage (amTarget):
5014 Push (a new CAwaitMessage with target amTarget) to result.
5015 When CListen (listenAddr, listenHandler):
5016 Push (a new CListen with addr {expr_entry}(listenAddr) and handler listenHandler) to result.
5017 When CConnectTo (connAddr, connTarget):
5018 Push (a new CConnectTo with addr {expr_entry}(connAddr) and target connTarget) to result.
5019 When CZone (zoneName, zoneKind, zoneBody):
5020 Push (a new CZone with name zoneName and kind zoneKind and body {wrapper_name}(zoneBody)) to result.
5021 Return result.
5022"#, wrapper_name = wrapper_name, expr_entry = expr_entry)
5023}
5024
5025fn replace_word(source: &str, from: &str, to: &str) -> String {
5026 let mut result = String::with_capacity(source.len());
5027 let mut remaining = source;
5028 while let Some(pos) = remaining.find(from) {
5029 let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
5030 let after_pos = pos + from.len();
5031 let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
5032 let is_word = !before.is_ascii_alphanumeric() && before != b'_'
5033 && !after.is_ascii_alphanumeric() && after != b'_';
5034 result.push_str(&remaining[..pos]);
5035 if is_word {
5036 result.push_str(to);
5037 } else {
5038 result.push_str(from);
5039 }
5040 remaining = &remaining[after_pos..];
5041 }
5042 result.push_str(remaining);
5043 result
5044}
5045
5046#[cfg(test)]
5047mod tests {
5048 use super::*;
5049
5050 #[test]
5051 fn test_compile_let_statement() {
5052 let source = "## Main\nLet x be 5.";
5053 let result = compile_to_rust(source);
5054 assert!(result.is_ok(), "Should compile: {:?}", result);
5055 let rust = result.unwrap();
5056 assert!(rust.contains("fn main()"));
5057 assert!(rust.contains("let x = 5;"));
5058 }
5059
5060 #[test]
5061 fn test_compile_return_statement() {
5062 let source = "## Main\nReturn 42.";
5063 let result = compile_to_rust(source);
5064 assert!(result.is_ok(), "Should compile: {:?}", result);
5065 let rust = result.unwrap();
5066 assert!(rust.contains("return 42;"));
5067 }
5068
5069}