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) => Some(format!("(a new CFloat with value {})", f)),
1475 Literal::Nothing => Some("(a new CText with value \"nothing\")".to_string()),
1476 _ => None,
1477 },
1478 Expr::Identifier(sym) => {
1479 let name = interner.resolve(*sym);
1480 Some(format!("(a new CVar with name \"{}\")", name))
1481 }
1482 Expr::Not { operand } => {
1483 if let Some(inner) = try_inline_expr(operand, interner) {
1484 Some(format!("(a new CNot with inner {})", inner))
1485 } else {
1486 None
1487 }
1488 }
1489 Expr::OptionNone => Some("(a new COptionNone)".to_string()),
1490 _ => None,
1491 }
1492}
1493
1494fn encode_expr_compact(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1495 if let Some(inline) = try_inline_expr(expr, interner) {
1497 return inline;
1498 }
1499
1500 let var = format!("e_{}", *counter);
1502 *counter += 1;
1503
1504 match expr {
1505 Expr::BinaryOp { op, left, right } => {
1506 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1507 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1508 let op_str = match op {
1509 BinaryOpKind::Add => "+",
1510 BinaryOpKind::Subtract => "-",
1511 BinaryOpKind::Multiply => "*",
1512 BinaryOpKind::Divide => "/",
1513 BinaryOpKind::Modulo => "%",
1514 BinaryOpKind::Eq => "==",
1515 BinaryOpKind::NotEq => "!=",
1516 BinaryOpKind::Lt => "<",
1517 BinaryOpKind::Gt => ">",
1518 BinaryOpKind::LtEq => "<=",
1519 BinaryOpKind::GtEq => ">=",
1520 BinaryOpKind::And => "&&",
1521 BinaryOpKind::Or => "||",
1522 BinaryOpKind::Concat => "+",
1523 BinaryOpKind::BitXor => "^",
1524 BinaryOpKind::Shl => "<<",
1525 BinaryOpKind::Shr => ">>",
1526 };
1527 output.push_str(&format!(
1528 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
1529 var, op_str, left_var, right_var
1530 ));
1531 }
1532 Expr::Call { function, args } => {
1533 let fn_name = interner.resolve(*function);
1534 if let Some(field_names) = variants.get(fn_name) {
1535 let names_var = format!("nvNames_{}", *counter);
1536 *counter += 1;
1537 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1538 let vals_var = format!("nvVals_{}", *counter);
1539 *counter += 1;
1540 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1541 for (i, arg) in args.iter().enumerate() {
1542 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
1543 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1544 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1545 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
1546 }
1547 output.push_str(&format!(
1548 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1549 var, fn_name, names_var, vals_var
1550 ));
1551 } else {
1552 let args_var = format!("callArgs_{}", *counter);
1553 *counter += 1;
1554 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1555 for arg in args {
1556 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1557 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1558 }
1559 output.push_str(&format!(
1560 "Let {} be a new CCall with name \"{}\" and args {}.\n",
1561 var, fn_name, args_var
1562 ));
1563 }
1564 }
1565 Expr::Index { collection, index } => {
1566 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1567 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1568 output.push_str(&format!(
1569 "Let {} be a new CIndex with coll {} and idx {}.\n",
1570 var, coll_var, idx_var
1571 ));
1572 }
1573 Expr::Length { collection } => {
1574 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1575 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
1576 }
1577 Expr::FieldAccess { object, field } => {
1578 let obj_var = encode_expr_compact(object, counter, output, interner, variants);
1579 let field_name = interner.resolve(*field);
1580 output.push_str(&format!(
1581 "Let {} be a new CMapGet with target {} and key (a new CText with value \"{}\").\n",
1582 var, obj_var, field_name
1583 ));
1584 }
1585 Expr::NewVariant { variant, fields, .. } => {
1586 let variant_name = interner.resolve(*variant);
1587 let names_var = format!("nvNames_{}", *counter);
1588 *counter += 1;
1589 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1590 let vals_var = format!("nvVals_{}", *counter);
1591 *counter += 1;
1592 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1593 for (field_name, field_expr) in fields {
1594 let fname = interner.resolve(*field_name);
1595 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1596 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1597 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1598 }
1599 output.push_str(&format!(
1600 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1601 var, variant_name, names_var, vals_var
1602 ));
1603 }
1604 Expr::New { type_name, init_fields, .. } => {
1605 let tn = interner.resolve(*type_name);
1606 if tn == "Seq" || tn == "List" {
1607 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
1608 } else if tn == "Set" {
1609 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
1610 } else {
1611 let names_var = format!("nvNames_{}", *counter);
1612 *counter += 1;
1613 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1614 let vals_var = format!("nvVals_{}", *counter);
1615 *counter += 1;
1616 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1617 for (field_name, field_expr) in init_fields {
1618 let fname = interner.resolve(*field_name);
1619 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1620 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1621 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1622 }
1623 output.push_str(&format!(
1624 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1625 var, tn, names_var, vals_var
1626 ));
1627 }
1628 }
1629 Expr::InterpolatedString(parts) => {
1630 if parts.is_empty() {
1631 output.push_str(&format!("Let {} be (a new CText with value \"\").\n", var));
1632 } else {
1633 let mut part_vars: Vec<String> = Vec::new();
1634 for part in parts {
1635 match part {
1636 StringPart::Literal(sym) => {
1637 let text = interner.resolve(*sym);
1638 part_vars.push(format!("(a new CText with value \"{}\")", text));
1639 }
1640 StringPart::Expr { value, .. } => {
1641 let pv = encode_expr_compact(value, counter, output, interner, variants);
1642 part_vars.push(pv);
1643 }
1644 }
1645 }
1646 if part_vars.len() == 1 {
1647 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
1648 } else {
1649 let mut acc = part_vars[0].clone();
1650 for pv in &part_vars[1..] {
1651 let concat_var = format!("e_{}", *counter);
1652 *counter += 1;
1653 output.push_str(&format!(
1654 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
1655 concat_var, acc, pv
1656 ));
1657 acc = concat_var;
1658 }
1659 output.push_str(&format!("Let {} be {}.\n", var, acc));
1660 }
1661 }
1662 }
1663 Expr::Range { start, end } => {
1664 let start_var = encode_expr_compact(start, counter, output, interner, variants);
1665 let end_var = encode_expr_compact(end, counter, output, interner, variants);
1666 output.push_str(&format!(
1667 "Let {} be a new CRange with start {} and end {}.\n",
1668 var, start_var, end_var
1669 ));
1670 }
1671 Expr::Copy { expr } => {
1672 let inner_var = encode_expr_compact(expr, counter, output, interner, variants);
1673 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
1674 }
1675 Expr::Contains { collection, value } => {
1676 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1677 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1678 output.push_str(&format!(
1679 "Let {} be a new CContains with coll {} and elem {}.\n",
1680 var, coll_var, val_var
1681 ));
1682 }
1683 Expr::OptionSome { value } => {
1684 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1685 output.push_str(&format!(
1686 "Let {} be a new COptionSome with inner {}.\n", var, inner_var
1687 ));
1688 }
1689 Expr::Tuple(elems) => {
1690 let items_var = format!("tupItems_{}", *counter);
1691 *counter += 1;
1692 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
1693 for elem in elems {
1694 let elem_var = encode_expr_compact(elem, counter, output, interner, variants);
1695 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
1696 }
1697 output.push_str(&format!(
1698 "Let {} be a new CTuple with items {}.\n", var, items_var
1699 ));
1700 }
1701 Expr::Closure { params, body, .. } => {
1702 let params_var = format!("clp_{}", *counter);
1703 *counter += 1;
1704 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1705 let mut param_names = HashSet::new();
1706 for (sym, _) in params {
1707 let name = interner.resolve(*sym);
1708 param_names.insert(name.to_string());
1709 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
1710 }
1711 let body_var = format!("clb_{}", *counter);
1712 *counter += 1;
1713 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
1714 match body {
1715 ClosureBody::Expression(e) => {
1716 let ret_expr = encode_expr_compact(e, counter, output, interner, variants);
1717 let ret_var = format!("s_{}", *counter);
1718 *counter += 1;
1719 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
1720 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
1721 }
1722 ClosureBody::Block(stmts) => {
1723 for s in stmts.iter() {
1724 let sv = encode_stmt_compact(s, counter, output, interner, variants);
1725 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
1726 }
1727 }
1728 }
1729 let bound: HashSet<String> = param_names;
1730 let free = collect_free_vars_expr(expr, interner, &bound);
1731 let cap_var = format!("clc_{}", *counter);
1732 *counter += 1;
1733 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
1734 for fv in &free {
1735 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
1736 }
1737 output.push_str(&format!(
1738 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
1739 var, params_var, body_var, cap_var
1740 ));
1741 }
1742 Expr::CallExpr { callee, args } => {
1743 let callee_var = encode_expr_compact(callee, counter, output, interner, variants);
1744 let args_var = format!("cea_{}", *counter);
1745 *counter += 1;
1746 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1747 for a in args {
1748 let av = encode_expr_compact(a, counter, output, interner, variants);
1749 output.push_str(&format!("Push {} to {}.\n", av, args_var));
1750 }
1751 output.push_str(&format!(
1752 "Let {} be a new CCallExpr with target {} and args {}.\n",
1753 var, callee_var, args_var
1754 ));
1755 }
1756 Expr::Slice { collection, start, end } => {
1757 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1758 let start_var = encode_expr_compact(start, counter, output, interner, variants);
1759 let end_var = encode_expr_compact(end, counter, output, interner, variants);
1760 output.push_str(&format!(
1761 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
1762 var, coll_var, start_var, end_var
1763 ));
1764 }
1765 Expr::Union { left, right } => {
1766 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1767 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1768 output.push_str(&format!(
1769 "Let {} be a new CUnion with left {} and right {}.\n",
1770 var, left_var, right_var
1771 ));
1772 }
1773 Expr::Intersection { left, right } => {
1774 let left_var = encode_expr_compact(left, counter, output, interner, variants);
1775 let right_var = encode_expr_compact(right, counter, output, interner, variants);
1776 output.push_str(&format!(
1777 "Let {} be a new CIntersection with left {} and right {}.\n",
1778 var, left_var, right_var
1779 ));
1780 }
1781 Expr::Give { value } => {
1782 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1783 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
1784 }
1785 Expr::Escape { code, .. } => {
1786 let code_str = interner.resolve(*code);
1787 output.push_str(&format!(
1788 "Let {} be a new CEscExpr with code \"{}\".\n",
1789 var, code_str.replace('\"', "\\\"")
1790 ));
1791 }
1792 _ => {
1793 output.push_str(&format!("Let {} be (a new CText with value \"unsupported\").\n", var));
1795 }
1796 }
1797
1798 var
1799}
1800
1801fn encode_stmt_compact(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1802 let var = format!("s_{}", *counter);
1803 *counter += 1;
1804
1805 match stmt {
1806 Stmt::Let { var: name, value, .. } => {
1807 let name_str = interner.resolve(*name);
1808 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1809 output.push_str(&format!(
1810 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
1811 var, name_str, expr_var
1812 ));
1813 }
1814 Stmt::Set { target, value } => {
1815 let name_str = interner.resolve(*target);
1816 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1817 output.push_str(&format!(
1818 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
1819 var, name_str, expr_var
1820 ));
1821 }
1822 Stmt::If { cond, then_block, else_block } => {
1823 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1824 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
1825 let then_var = encode_stmt_list_compact(&then_stmts, counter, output, interner, variants);
1826 let else_var = if let Some(els) = else_block {
1827 let else_stmts: Vec<&Stmt> = els.iter().collect();
1828 encode_stmt_list_compact(&else_stmts, counter, output, interner, variants)
1829 } else {
1830 let empty_var = format!("emptyBlock_{}", *counter);
1831 *counter += 1;
1832 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
1833 empty_var
1834 };
1835 output.push_str(&format!(
1836 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
1837 var, cond_var, then_var, else_var
1838 ));
1839 }
1840 Stmt::While { cond, body, .. } => {
1841 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1842 let body_stmts: Vec<&Stmt> = body.iter().collect();
1843 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1844 output.push_str(&format!(
1845 "Let {} be a new CWhile with cond {} and body {}.\n",
1846 var, cond_var, body_var
1847 ));
1848 }
1849 Stmt::Return { value } => {
1850 if let Some(val) = value {
1851 let expr_var = encode_expr_compact(val, counter, output, interner, variants);
1852 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
1853 } else {
1854 output.push_str(&format!("Let {} be a new CReturn with expr (a new CText with value \"nothing\").\n", var));
1855 }
1856 }
1857 Stmt::Show { object, .. } => {
1858 let expr_var = encode_expr_compact(object, counter, output, interner, variants);
1859 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
1860 }
1861 Stmt::Repeat { pattern, iterable, body } => {
1862 let var_str = match pattern {
1863 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
1864 Pattern::Tuple(syms) => {
1865 if let Some(s) = syms.first() {
1866 interner.resolve(*s).to_string()
1867 } else {
1868 "item".to_string()
1869 }
1870 }
1871 };
1872 let coll_var = encode_expr_compact(iterable, counter, output, interner, variants);
1873 let body_stmts: Vec<&Stmt> = body.iter().collect();
1874 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1875 output.push_str(&format!(
1876 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
1877 var, var_str, coll_var, body_var
1878 ));
1879 }
1880 Stmt::Push { value, collection } => {
1881 let coll_name = extract_ident_name(collection, interner);
1882 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1883 output.push_str(&format!(
1884 "Let {} be a new CPush with expr {} and target \"{}\".\n",
1885 var, expr_var, coll_name
1886 ));
1887 }
1888 Stmt::SetIndex { collection, index, value } => {
1889 let target_str = extract_ident_name(collection, interner);
1890 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1891 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1892 output.push_str(&format!(
1893 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
1894 var, target_str, idx_var, val_var
1895 ));
1896 }
1897 Stmt::SetField { object, field, value } => {
1898 let target_str = extract_ident_name(object, interner);
1899 let field_str = interner.resolve(*field);
1900 let val_var = encode_expr_compact(value, counter, output, interner, variants);
1901 output.push_str(&format!(
1902 "Let {} be a new CSetField with target \"{}\" and field \"{}\" and val {}.\n",
1903 var, target_str, field_str, val_var
1904 ));
1905 }
1906 Stmt::Break => {
1907 output.push_str(&format!("Let {} be a new CBreak.\n", var));
1908 }
1909 Stmt::Inspect { target, arms, .. } => {
1910 let target_var = encode_expr_compact(target, counter, output, interner, variants);
1911 let arms_var = format!("arms_{}", *counter);
1912 *counter += 1;
1913 output.push_str(&format!("Let {} be a new Seq of CMatchArm.\n", arms_var));
1914 for arm in arms {
1915 if let Some(variant_sym) = arm.variant {
1916 let vname = interner.resolve(variant_sym);
1917 let bindings_var = format!("bindings_{}", *counter);
1918 *counter += 1;
1919 output.push_str(&format!("Let {} be a new Seq of Text.\n", bindings_var));
1920 for (_, binding_name) in &arm.bindings {
1921 let bn = interner.resolve(*binding_name);
1922 output.push_str(&format!("Push \"{}\" to {}.\n", bn, bindings_var));
1923 }
1924 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1925 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1926 let arm_var = format!("arm_{}", *counter);
1927 *counter += 1;
1928 output.push_str(&format!(
1929 "Let {} be a new CWhen with variantName \"{}\" and bindings {} and body {}.\n",
1930 arm_var, vname, bindings_var, body_var
1931 ));
1932 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1933 } else {
1934 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1935 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1936 let arm_var = format!("arm_{}", *counter);
1937 *counter += 1;
1938 output.push_str(&format!(
1939 "Let {} be a new COtherwise with body {}.\n",
1940 arm_var, body_var
1941 ));
1942 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1943 }
1944 }
1945 output.push_str(&format!(
1946 "Let {} be a new CInspect with target {} and arms {}.\n",
1947 var, target_var, arms_var
1948 ));
1949 }
1950 _ => {
1951 return encode_stmt_src(stmt, counter, output, interner, variants);
1953 }
1954 }
1955
1956 var
1957}
1958
1959fn encode_stmt_list_compact(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1960 let list_var = format!("stmts_{}", *counter);
1961 *counter += 1;
1962 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
1963 for s in stmts {
1964 let sv = encode_stmt_compact(s, counter, output, interner, variants);
1965 output.push_str(&format!("Push {} to {}.\n", sv, list_var));
1966 }
1967 list_var
1968}
1969
1970fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1971 let mut free = HashSet::new();
1972 match expr {
1973 Expr::Identifier(sym) => {
1974 let name = interner.resolve(*sym).to_string();
1975 if !bound.contains(&name) {
1976 free.insert(name);
1977 }
1978 }
1979 Expr::BinaryOp { left, right, .. } => {
1980 free.extend(collect_free_vars_expr(left, interner, bound));
1981 free.extend(collect_free_vars_expr(right, interner, bound));
1982 }
1983 Expr::Not { operand } => {
1984 free.extend(collect_free_vars_expr(operand, interner, bound));
1985 }
1986 Expr::Copy { expr: inner } => {
1987 free.extend(collect_free_vars_expr(inner, interner, bound));
1988 }
1989 Expr::CallExpr { callee, args } => {
1990 free.extend(collect_free_vars_expr(callee, interner, bound));
1991 for a in args {
1992 free.extend(collect_free_vars_expr(a, interner, bound));
1993 }
1994 }
1995 Expr::Index { collection, index } => {
1996 free.extend(collect_free_vars_expr(collection, interner, bound));
1997 free.extend(collect_free_vars_expr(index, interner, bound));
1998 }
1999 Expr::InterpolatedString(parts) => {
2000 for part in parts {
2001 if let StringPart::Expr { value, .. } = part {
2002 free.extend(collect_free_vars_expr(value, interner, bound));
2003 }
2004 }
2005 }
2006 Expr::Closure { params, body, .. } => {
2007 let mut inner_bound = bound.clone();
2008 for (sym, _) in params {
2009 inner_bound.insert(interner.resolve(*sym).to_string());
2010 }
2011 match body {
2012 ClosureBody::Expression(e) => {
2013 free.extend(collect_free_vars_expr(e, interner, &inner_bound));
2014 }
2015 ClosureBody::Block(stmts) => {
2016 for s in stmts.iter() {
2017 free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
2018 }
2019 }
2020 }
2021 }
2022 _ => {}
2023 }
2024 free
2025}
2026
2027fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
2028 let mut free = HashSet::new();
2029 match stmt {
2030 Stmt::Let { var, value, .. } => {
2031 free.extend(collect_free_vars_expr(value, interner, bound));
2032 }
2033 Stmt::Set { target, value, .. } => {
2034 let n = interner.resolve(*target).to_string();
2035 if !bound.contains(&n) {
2036 free.insert(n);
2037 }
2038 free.extend(collect_free_vars_expr(value, interner, bound));
2039 }
2040 Stmt::Show { object, .. } => {
2041 free.extend(collect_free_vars_expr(object, interner, bound));
2042 }
2043 Stmt::Return { value } => {
2044 if let Some(v) = value {
2045 free.extend(collect_free_vars_expr(v, interner, bound));
2046 }
2047 }
2048 _ => {}
2049 }
2050 free
2051}
2052
2053fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2054 let var = format!("e_{}", *counter);
2055 *counter += 1;
2056
2057 match expr {
2058 Expr::Literal(lit) => match lit {
2059 Literal::Number(n) => {
2060 output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
2061 }
2062 Literal::Boolean(b) => {
2063 output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
2064 }
2065 Literal::Text(s) => {
2066 let text = interner.resolve(*s);
2067 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
2068 }
2069 Literal::Float(f) => {
2070 output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, f));
2071 }
2072 Literal::Duration(nanos) => {
2073 let millis = nanos / 1_000_000;
2074 let amount_var = format!("e_{}", *counter);
2075 *counter += 1;
2076 output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
2077 output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
2078 }
2079 Literal::Nothing => {
2080 output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
2081 }
2082 _ => {
2083 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2084 }
2085 },
2086 Expr::Identifier(sym) => {
2087 let name = interner.resolve(*sym);
2088 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
2089 }
2090 Expr::BinaryOp { op, left, right } => {
2091 let left_var = encode_expr_src(left, counter, output, interner, variants);
2092 let right_var = encode_expr_src(right, counter, output, interner, variants);
2093 let op_str = match op {
2094 BinaryOpKind::Add => "+",
2095 BinaryOpKind::Subtract => "-",
2096 BinaryOpKind::Multiply => "*",
2097 BinaryOpKind::Divide => "/",
2098 BinaryOpKind::Modulo => "%",
2099 BinaryOpKind::Eq => "==",
2100 BinaryOpKind::NotEq => "!=",
2101 BinaryOpKind::Lt => "<",
2102 BinaryOpKind::Gt => ">",
2103 BinaryOpKind::LtEq => "<=",
2104 BinaryOpKind::GtEq => ">=",
2105 BinaryOpKind::And => "&&",
2106 BinaryOpKind::Or => "||",
2107 BinaryOpKind::Concat => "+",
2108 BinaryOpKind::BitXor => "^",
2109 BinaryOpKind::Shl => "<<",
2110 BinaryOpKind::Shr => ">>",
2111 };
2112 output.push_str(&format!(
2113 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
2114 var, op_str, left_var, right_var
2115 ));
2116 }
2117 Expr::Not { operand } => {
2118 let inner_var = encode_expr_src(operand, counter, output, interner, variants);
2119 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
2120 }
2121 Expr::Call { function, args } => {
2122 let fn_name = interner.resolve(*function);
2123 if let Some(field_names) = variants.get(fn_name) {
2124 let names_var = format!("nvNames_{}", *counter);
2126 *counter += 1;
2127 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2128 let vals_var = format!("nvVals_{}", *counter);
2129 *counter += 1;
2130 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2131 for (i, arg) in args.iter().enumerate() {
2132 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
2133 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2134 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2135 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
2136 }
2137 output.push_str(&format!(
2138 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2139 var, fn_name, names_var, vals_var
2140 ));
2141 } else {
2142 let args_var = format!("callArgs_{}", *counter);
2143 *counter += 1;
2144 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2145 for arg in args {
2146 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2147 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2148 }
2149 output.push_str(&format!(
2150 "Let {} be a new CCall with name \"{}\" and args {}.\n",
2151 var, fn_name, args_var
2152 ));
2153 }
2154 }
2155 Expr::Index { collection, index } => {
2156 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2157 let idx_var = encode_expr_src(index, counter, output, interner, variants);
2158 output.push_str(&format!(
2159 "Let {} be a new CIndex with coll {} and idx {}.\n",
2160 var, coll_var, idx_var
2161 ));
2162 }
2163 Expr::Length { collection } => {
2164 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2165 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
2166 }
2167 Expr::FieldAccess { object, field } => {
2168 let obj_var = encode_expr_src(object, counter, output, interner, variants);
2169 let field_name = interner.resolve(*field);
2170 let key_var = format!("e_{}", *counter);
2171 *counter += 1;
2172 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2173 output.push_str(&format!(
2174 "Let {} be a new CMapGet with target {} and key {}.\n",
2175 var, obj_var, key_var
2176 ));
2177 }
2178 Expr::NewVariant { variant, fields, .. } => {
2179 let variant_name = interner.resolve(*variant);
2180 let names_var = format!("nvNames_{}", *counter);
2181 *counter += 1;
2182 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2183 let vals_var = format!("nvVals_{}", *counter);
2184 *counter += 1;
2185 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2186 for (field_name, field_expr) in fields {
2187 let fname = interner.resolve(*field_name);
2188 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2189 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2190 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2191 }
2192 output.push_str(&format!(
2193 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2194 var, variant_name, names_var, vals_var
2195 ));
2196 }
2197 Expr::New { type_name, init_fields, .. } => {
2198 let tn = interner.resolve(*type_name);
2199 if tn == "Seq" || tn == "List" {
2200 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
2201 } else if tn == "Set" {
2202 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
2203 } else if init_fields.is_empty() {
2204 let names_var = format!("nvNames_{}", *counter);
2205 *counter += 1;
2206 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2207 let vals_var = format!("nvVals_{}", *counter);
2208 *counter += 1;
2209 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2210 output.push_str(&format!(
2211 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2212 var, tn, names_var, vals_var
2213 ));
2214 } else {
2215 let names_var = format!("nvNames_{}", *counter);
2216 *counter += 1;
2217 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2218 let vals_var = format!("nvVals_{}", *counter);
2219 *counter += 1;
2220 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2221 for (field_name, field_expr) in init_fields {
2222 let fname = interner.resolve(*field_name);
2223 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2224 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2225 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2226 }
2227 output.push_str(&format!(
2228 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2229 var, tn, names_var, vals_var
2230 ));
2231 }
2232 }
2233 Expr::InterpolatedString(parts) => {
2234 if parts.is_empty() {
2235 output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
2236 } else {
2237 let mut part_vars: Vec<String> = Vec::new();
2238 for part in parts {
2239 match part {
2240 StringPart::Literal(sym) => {
2241 let text = interner.resolve(*sym);
2242 let pv = format!("e_{}", *counter);
2243 *counter += 1;
2244 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", pv, text));
2245 part_vars.push(pv);
2246 }
2247 StringPart::Expr { value, .. } => {
2248 let pv = encode_expr_src(value, counter, output, interner, variants);
2249 part_vars.push(pv);
2250 }
2251 }
2252 }
2253 if part_vars.len() == 1 {
2254 output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
2255 } else {
2256 let mut acc = part_vars[0].clone();
2257 for pv in &part_vars[1..] {
2258 let concat_var = format!("e_{}", *counter);
2259 *counter += 1;
2260 output.push_str(&format!(
2261 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
2262 concat_var, acc, pv
2263 ));
2264 acc = concat_var;
2265 }
2266 output.push_str(&format!("Let {} be {}.\n", var, acc));
2267 }
2268 }
2269 }
2270 Expr::Range { start, end } => {
2271 let start_var = encode_expr_src(start, counter, output, interner, variants);
2272 let end_var = encode_expr_src(end, counter, output, interner, variants);
2273 output.push_str(&format!(
2274 "Let {} be a new CRange with start {} and end {}.\n",
2275 var, start_var, end_var
2276 ));
2277 }
2278 Expr::Slice { collection, start, end } => {
2279 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2280 let start_var = encode_expr_src(start, counter, output, interner, variants);
2281 let end_var = encode_expr_src(end, counter, output, interner, variants);
2282 output.push_str(&format!(
2283 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
2284 var, coll_var, start_var, end_var
2285 ));
2286 }
2287 Expr::Copy { expr } => {
2288 let inner_var = encode_expr_src(expr, counter, output, interner, variants);
2289 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
2290 }
2291 Expr::Contains { collection, value } => {
2292 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2293 let val_var = encode_expr_src(value, counter, output, interner, variants);
2294 output.push_str(&format!(
2295 "Let {} be a new CContains with coll {} and elem {}.\n",
2296 var, coll_var, val_var
2297 ));
2298 }
2299 Expr::Union { left, right } => {
2300 let left_var = encode_expr_src(left, counter, output, interner, variants);
2301 let right_var = encode_expr_src(right, counter, output, interner, variants);
2302 output.push_str(&format!(
2303 "Let {} be a new CUnion with left {} and right {}.\n",
2304 var, left_var, right_var
2305 ));
2306 }
2307 Expr::Intersection { left, right } => {
2308 let left_var = encode_expr_src(left, counter, output, interner, variants);
2309 let right_var = encode_expr_src(right, counter, output, interner, variants);
2310 output.push_str(&format!(
2311 "Let {} be a new CIntersection with left {} and right {}.\n",
2312 var, left_var, right_var
2313 ));
2314 }
2315 Expr::OptionSome { value } => {
2316 let inner_var = encode_expr_src(value, counter, output, interner, variants);
2317 output.push_str(&format!(
2318 "Let {} be a new COptionSome with inner {}.\n",
2319 var, inner_var
2320 ));
2321 }
2322 Expr::OptionNone => {
2323 output.push_str(&format!("Let {} be a new COptionNone.\n", var));
2324 }
2325 Expr::Tuple(elems) => {
2326 let items_var = format!("tupItems_{}", *counter);
2327 *counter += 1;
2328 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2329 for elem in elems {
2330 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
2331 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2332 }
2333 output.push_str(&format!(
2334 "Let {} be a new CTuple with items {}.\n",
2335 var, items_var
2336 ));
2337 }
2338 Expr::Closure { params, body, .. } => {
2339 let params_var = format!("clp_{}", *counter);
2340 *counter += 1;
2341 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2342 let mut param_names = HashSet::new();
2343 for (sym, _) in params {
2344 let name = interner.resolve(*sym);
2345 param_names.insert(name.to_string());
2346 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
2347 }
2348 let body_var = format!("clb_{}", *counter);
2349 *counter += 1;
2350 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2351 match body {
2352 ClosureBody::Expression(e) => {
2353 let ret_expr = encode_expr_src(e, counter, output, interner, variants);
2354 let ret_var = format!("s_{}", *counter);
2355 *counter += 1;
2356 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
2357 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
2358 }
2359 ClosureBody::Block(stmts) => {
2360 for s in stmts.iter() {
2361 let sv = encode_stmt_src(s, counter, output, interner, variants);
2362 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2363 }
2364 }
2365 }
2366 let bound: HashSet<String> = param_names;
2367 let free = collect_free_vars_expr(expr, interner, &bound);
2368 let cap_var = format!("clc_{}", *counter);
2369 *counter += 1;
2370 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
2371 for fv in &free {
2372 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
2373 }
2374 output.push_str(&format!(
2375 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
2376 var, params_var, body_var, cap_var
2377 ));
2378 }
2379 Expr::CallExpr { callee, args } => {
2380 let callee_var = encode_expr_src(callee, counter, output, interner, variants);
2381 let args_var = format!("cea_{}", *counter);
2382 *counter += 1;
2383 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2384 for a in args {
2385 let av = encode_expr_src(a, counter, output, interner, variants);
2386 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2387 }
2388 output.push_str(&format!(
2389 "Let {} be a new CCallExpr with target {} and args {}.\n",
2390 var, callee_var, args_var
2391 ));
2392 }
2393 Expr::Give { value } => {
2394 let inner_var = encode_expr_src(value, counter, output, interner, variants);
2395 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
2396 }
2397 Expr::Escape { code, .. } => {
2398 let code_str = interner.resolve(*code);
2399 output.push_str(&format!(
2400 "Let {} be a new CEscExpr with code \"{}\".\n",
2401 var, code_str.replace('\"', "\\\"")
2402 ));
2403 }
2404 _ => {
2405 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2406 }
2407 }
2408
2409 var
2410}
2411
2412fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2413 let var = format!("s_{}", *counter);
2414 *counter += 1;
2415
2416 match stmt {
2417 Stmt::Let { var: name, value, .. } => {
2418 let name_str = interner.resolve(*name);
2419 let expr_var = encode_expr_src(value, counter, output, interner, variants);
2420 output.push_str(&format!(
2421 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2422 var, name_str, expr_var
2423 ));
2424 }
2425 Stmt::Set { target, value } => {
2426 let name_str = interner.resolve(*target);
2427 let expr_var = encode_expr_src(value, counter, output, interner, variants);
2428 output.push_str(&format!(
2429 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2430 var, name_str, expr_var
2431 ));
2432 }
2433 Stmt::If { cond, then_block, else_block } => {
2434 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2435 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
2436 let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
2437 let else_var = if let Some(els) = else_block {
2438 let else_stmts: Vec<&Stmt> = els.iter().collect();
2439 encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
2440 } else {
2441 let empty_var = format!("emptyBlock_{}", *counter);
2442 *counter += 1;
2443 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
2444 empty_var
2445 };
2446 output.push_str(&format!(
2447 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2448 var, cond_var, then_var, else_var
2449 ));
2450 }
2451 Stmt::While { cond, body, .. } => {
2452 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2453 let body_stmts: Vec<&Stmt> = body.iter().collect();
2454 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
2455 output.push_str(&format!(
2456 "Let {} be a new CWhile with cond {} and body {}.\n",
2457 var, cond_var, body_var
2458 ));
2459 }
2460 Stmt::Return { value } => {
2461 if let Some(expr) = value {
2462 let expr_var = encode_expr_src(expr, counter, output, interner, variants);
2463 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
2464 } else {
2465 let nothing_var = format!("e_{}", *counter);
2466 *counter += 1;
2467 output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
2468 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
2469 }
2470 }
2471 Stmt::Show { object, .. } => {
2472 let expr_var = encode_expr_src(object, counter, output, interner, variants);
2473 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
2474 }
2475 Stmt::Call { function, args } => {
2476 let fn_name = interner.resolve(*function);
2477 let args_var = format!("callSArgs_{}", *counter);
2478 *counter += 1;
2479 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2480 for arg in args {
2481 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2482 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2483 }
2484 output.push_str(&format!(
2485 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2486 var, fn_name, args_var
2487 ));
2488 }
2489 Stmt::Push { value, collection } => {
2490 let val_var = encode_expr_src(value, counter, output, interner, variants);
2491 let coll_name = extract_ident_name(collection, interner);
2492 output.push_str(&format!(
2493 "Let {} be a new CPush with expr {} and target \"{}\".\n",
2494 var, val_var, coll_name
2495 ));
2496 }
2497 Stmt::SetIndex { collection, index, value } => {
2498 let coll_name = extract_ident_name(collection, interner);
2499 let idx_var = encode_expr_src(index, counter, output, interner, variants);
2500 let val_var = encode_expr_src(value, counter, output, interner, variants);
2501 output.push_str(&format!(
2502 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
2503 var, coll_name, idx_var, val_var
2504 ));
2505 }
2506 Stmt::SetField { object, field, value } => {
2507 let map_name = extract_ident_name(object, interner);
2508 let field_name = interner.resolve(*field);
2509 let key_var = format!("e_{}", *counter);
2510 *counter += 1;
2511 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2512 let val_var = encode_expr_src(value, counter, output, interner, variants);
2513 output.push_str(&format!(
2514 "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
2515 var, map_name, key_var, val_var
2516 ));
2517 }
2518 Stmt::Pop { collection, .. } => {
2519 let coll_name = extract_ident_name(collection, interner);
2520 output.push_str(&format!(
2521 "Let {} be a new CPop with target \"{}\".\n",
2522 var, coll_name
2523 ));
2524 }
2525 Stmt::Add { value, collection } => {
2526 let val_var = encode_expr_src(value, counter, output, interner, variants);
2527 let coll_name = extract_ident_name(collection, interner);
2528 output.push_str(&format!(
2529 "Let {} be a new CAdd with elem {} and target \"{}\".\n",
2530 var, val_var, coll_name
2531 ));
2532 }
2533 Stmt::Remove { value, collection } => {
2534 let val_var = encode_expr_src(value, counter, output, interner, variants);
2535 let coll_name = extract_ident_name(collection, interner);
2536 output.push_str(&format!(
2537 "Let {} be a new CRemove with elem {} and target \"{}\".\n",
2538 var, val_var, coll_name
2539 ));
2540 }
2541 Stmt::Inspect { .. } => {
2542 return String::new(); }
2544 Stmt::Repeat { .. } => {
2545 return String::new(); }
2547 Stmt::Break => {
2548 output.push_str(&format!("Let {} be a new CBreak.\n", var));
2549 }
2550 Stmt::RuntimeAssert { condition, .. } => {
2551 let cond_var = encode_expr_src(condition, counter, output, interner, variants);
2552 let msg_var = format!("e_{}", *counter);
2553 *counter += 1;
2554 output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
2555 output.push_str(&format!(
2556 "Let {} be a new CRuntimeAssert with cond {} and msg {}.\n",
2557 var, cond_var, msg_var
2558 ));
2559 }
2560 Stmt::Give { object, recipient } => {
2561 let expr_var = encode_expr_src(object, counter, output, interner, variants);
2562 let target_name = extract_ident_name(recipient, interner);
2563 output.push_str(&format!(
2564 "Let {} be a new CGive with expr {} and target \"{}\".\n",
2565 var, expr_var, target_name
2566 ));
2567 }
2568 Stmt::Escape { code, .. } => {
2569 let code_str = interner.resolve(*code);
2570 output.push_str(&format!(
2571 "Let {} be a new CEscStmt with code \"{}\".\n",
2572 var, code_str.replace('\"', "\\\"")
2573 ));
2574 }
2575 Stmt::Sleep { milliseconds } => {
2576 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2577 output.push_str(&format!(
2578 "Let {} be a new CSleep with duration {}.\n",
2579 var, dur_var
2580 ));
2581 }
2582 Stmt::ReadFrom { var: read_var, source } => {
2583 let var_name = interner.resolve(*read_var);
2584 match source {
2585 ReadSource::Console => {
2586 output.push_str(&format!(
2587 "Let {} be a new CReadConsole with target \"{}\".\n",
2588 var, var_name
2589 ));
2590 }
2591 ReadSource::File(path_expr) => {
2592 let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
2593 output.push_str(&format!(
2594 "Let {} be a new CReadFile with path {} and target \"{}\".\n",
2595 var, path_var, var_name
2596 ));
2597 }
2598 }
2599 }
2600 Stmt::WriteFile { content, path } => {
2601 let path_var = encode_expr_src(path, counter, output, interner, variants);
2602 let content_var = encode_expr_src(content, counter, output, interner, variants);
2603 output.push_str(&format!(
2604 "Let {} be a new CWriteFile with path {} and content {}.\n",
2605 var, path_var, content_var
2606 ));
2607 }
2608 Stmt::Check { source_text, .. } => {
2609 let pred_var = format!("e_{}", *counter);
2610 *counter += 1;
2611 output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
2612 let msg_var = format!("e_{}", *counter);
2613 *counter += 1;
2614 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
2615 output.push_str(&format!(
2616 "Let {} be a new CCheck with predicate {} and msg {}.\n",
2617 var, pred_var, msg_var
2618 ));
2619 }
2620 Stmt::Assert { .. } => {
2621 let prop_var = format!("e_{}", *counter);
2622 *counter += 1;
2623 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2624 output.push_str(&format!(
2625 "Let {} be a new CAssert with proposition {}.\n",
2626 var, prop_var
2627 ));
2628 }
2629 Stmt::Trust { justification, .. } => {
2630 let prop_var = format!("e_{}", *counter);
2631 *counter += 1;
2632 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2633 let just_str = interner.resolve(*justification);
2634 output.push_str(&format!(
2635 "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
2636 var, prop_var, just_str
2637 ));
2638 }
2639 Stmt::Require { crate_name, .. } => {
2640 let dep_name = interner.resolve(*crate_name);
2641 output.push_str(&format!(
2642 "Let {} be a new CRequire with dependency \"{}\".\n",
2643 var, dep_name
2644 ));
2645 }
2646 Stmt::MergeCrdt { source, target } => {
2647 let source_var = encode_expr_src(source, counter, output, interner, variants);
2648 let target_name = match target {
2649 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2650 _ => "unknown".to_string(),
2651 };
2652 output.push_str(&format!(
2653 "Let {} be a new CMerge with target \"{}\" and other {}.\n",
2654 var, target_name, source_var
2655 ));
2656 }
2657 Stmt::IncreaseCrdt { object, field, amount } => {
2658 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2659 let target_name = match object {
2660 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2661 _ => "unknown".to_string(),
2662 };
2663 output.push_str(&format!(
2664 "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
2665 var, target_name, amount_var
2666 ));
2667 }
2668 Stmt::DecreaseCrdt { object, field, amount } => {
2669 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2670 let target_name = match object {
2671 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2672 _ => "unknown".to_string(),
2673 };
2674 output.push_str(&format!(
2675 "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
2676 var, target_name, amount_var
2677 ));
2678 }
2679 Stmt::AppendToSequence { sequence, value } => {
2680 let value_var = encode_expr_src(value, counter, output, interner, variants);
2681 let target_name = match sequence {
2682 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2683 _ => "unknown".to_string(),
2684 };
2685 output.push_str(&format!(
2686 "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
2687 var, target_name, value_var
2688 ));
2689 }
2690 Stmt::ResolveConflict { object, .. } => {
2691 let target_name = match object {
2692 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2693 _ => "unknown".to_string(),
2694 };
2695 output.push_str(&format!(
2696 "Let {} be a new CResolve with target \"{}\".\n",
2697 var, target_name
2698 ));
2699 }
2700 Stmt::Sync { var: sync_var, topic } => {
2701 let topic_var = encode_expr_src(topic, counter, output, interner, variants);
2702 let var_name = interner.resolve(*sync_var);
2703 output.push_str(&format!(
2704 "Let {} be a new CSync with target \"{}\" and channel {}.\n",
2705 var, var_name, topic_var
2706 ));
2707 }
2708 Stmt::Mount { var: mount_var, path } => {
2709 let path_var = encode_expr_src(path, counter, output, interner, variants);
2710 let var_name = interner.resolve(*mount_var);
2711 output.push_str(&format!(
2712 "Let {} be a new CMount with target \"{}\" and path {}.\n",
2713 var, var_name, path_var
2714 ));
2715 }
2716 Stmt::Concurrent { tasks } => {
2717 let branches_var = format!("e_{}", *counter);
2718 *counter += 1;
2719 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2720 let branch_var = format!("e_{}", *counter);
2721 *counter += 1;
2722 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2723 for stmt in tasks.iter() {
2724 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2725 if !sv.is_empty() {
2726 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2727 }
2728 }
2729 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2730 output.push_str(&format!(
2731 "Let {} be a new CConcurrent with branches {}.\n",
2732 var, branches_var
2733 ));
2734 }
2735 Stmt::Parallel { tasks } => {
2736 let branches_var = format!("e_{}", *counter);
2737 *counter += 1;
2738 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2739 let branch_var = format!("e_{}", *counter);
2740 *counter += 1;
2741 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2742 for stmt in tasks.iter() {
2743 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2744 if !sv.is_empty() {
2745 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2746 }
2747 }
2748 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2749 output.push_str(&format!(
2750 "Let {} be a new CParallel with branches {}.\n",
2751 var, branches_var
2752 ));
2753 }
2754 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
2755 let func_name = interner.resolve(*function);
2756 let args_var = format!("e_{}", *counter);
2757 *counter += 1;
2758 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2759 for arg in args {
2760 let av = encode_expr_src(arg, counter, output, interner, variants);
2761 output.push_str(&format!("Push {} to {}.\n", av, args_var));
2762 }
2763 let body_var = format!("e_{}", *counter);
2764 *counter += 1;
2765 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2766 let call_var = format!("e_{}", *counter);
2767 *counter += 1;
2768 output.push_str(&format!(
2769 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2770 call_var, func_name, args_var
2771 ));
2772 output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
2773 let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
2774 interner.resolve(*handle).to_string()
2775 } else {
2776 "_task".to_string()
2777 };
2778 output.push_str(&format!(
2779 "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
2780 var, body_var, handle_name
2781 ));
2782 }
2783 Stmt::StopTask { handle } => {
2784 let handle_var = encode_expr_src(handle, counter, output, interner, variants);
2785 output.push_str(&format!(
2786 "Let {} be a new CStopTask with handle {}.\n",
2787 var, handle_var
2788 ));
2789 }
2790 Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
2791 let cap = capacity.unwrap_or(32);
2792 let cap_var = format!("e_{}", *counter);
2793 *counter += 1;
2794 output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
2795 let pipe_name = interner.resolve(*pipe_var);
2796 output.push_str(&format!(
2797 "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
2798 var, pipe_name, cap_var
2799 ));
2800 }
2801 Stmt::SendPipe { value, pipe } => {
2802 let val_var = encode_expr_src(value, counter, output, interner, variants);
2803 let pipe_name = match pipe {
2804 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2805 _ => "pipe".to_string(),
2806 };
2807 output.push_str(&format!(
2808 "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
2809 var, pipe_name, val_var
2810 ));
2811 }
2812 Stmt::ReceivePipe { var: recv_var, pipe } => {
2813 let recv_name = interner.resolve(*recv_var);
2814 let pipe_name = match pipe {
2815 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2816 _ => "pipe".to_string(),
2817 };
2818 output.push_str(&format!(
2819 "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
2820 var, pipe_name, recv_name
2821 ));
2822 }
2823 Stmt::TrySendPipe { value, pipe, .. } => {
2824 let val_var = encode_expr_src(value, counter, output, interner, variants);
2825 let pipe_name = match pipe {
2826 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2827 _ => "pipe".to_string(),
2828 };
2829 output.push_str(&format!(
2830 "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
2831 var, pipe_name, val_var
2832 ));
2833 }
2834 Stmt::TryReceivePipe { var: recv_var, pipe } => {
2835 let recv_name = interner.resolve(*recv_var);
2836 let pipe_name = match pipe {
2837 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2838 _ => "pipe".to_string(),
2839 };
2840 output.push_str(&format!(
2841 "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
2842 var, pipe_name, recv_name
2843 ));
2844 }
2845 Stmt::Select { branches } => {
2846 let branches_var = format!("e_{}", *counter);
2847 *counter += 1;
2848 output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
2849 for branch in branches {
2850 match branch {
2851 SelectBranch::Receive { var: recv_var, pipe, body } => {
2852 let recv_name = interner.resolve(*recv_var);
2853 let pipe_name = match pipe {
2854 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2855 _ => "pipe".to_string(),
2856 };
2857 let body_var = format!("e_{}", *counter);
2858 *counter += 1;
2859 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2860 for stmt in body.iter() {
2861 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2862 if !sv.is_empty() {
2863 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2864 }
2865 }
2866 let branch_var = format!("e_{}", *counter);
2867 *counter += 1;
2868 output.push_str(&format!(
2869 "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
2870 branch_var, pipe_name, recv_name, body_var
2871 ));
2872 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2873 }
2874 SelectBranch::Timeout { milliseconds, body } => {
2875 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2876 let body_var = format!("e_{}", *counter);
2877 *counter += 1;
2878 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2879 for stmt in body.iter() {
2880 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2881 if !sv.is_empty() {
2882 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2883 }
2884 }
2885 let branch_var = format!("e_{}", *counter);
2886 *counter += 1;
2887 output.push_str(&format!(
2888 "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
2889 branch_var, dur_var, body_var
2890 ));
2891 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2892 }
2893 }
2894 }
2895 output.push_str(&format!(
2896 "Let {} be a new CSelect with branches {}.\n",
2897 var, branches_var
2898 ));
2899 }
2900 Stmt::Spawn { agent_type, name } => {
2901 let agent_name = interner.resolve(*agent_type);
2902 let target_name = interner.resolve(*name);
2903 output.push_str(&format!(
2904 "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
2905 var, agent_name, target_name
2906 ));
2907 }
2908 Stmt::SendMessage { message, destination } => {
2909 let target_var = encode_expr_src(destination, counter, output, interner, variants);
2910 let msg_var = encode_expr_src(message, counter, output, interner, variants);
2911 output.push_str(&format!(
2912 "Let {} be a new CSendMessage with target {} and msg {}.\n",
2913 var, target_var, msg_var
2914 ));
2915 }
2916 Stmt::AwaitMessage { into, .. } => {
2917 let await_name = interner.resolve(*into);
2918 output.push_str(&format!(
2919 "Let {} be a new CAwaitMessage with target \"{}\".\n",
2920 var, await_name
2921 ));
2922 }
2923 Stmt::Listen { address } => {
2924 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2925 output.push_str(&format!(
2926 "Let {} be a new CListen with addr {} and handler \"default\".\n",
2927 var, addr_var
2928 ));
2929 }
2930 Stmt::ConnectTo { address } => {
2931 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2932 output.push_str(&format!(
2933 "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
2934 var, addr_var
2935 ));
2936 }
2937 Stmt::Zone { name, body, .. } => {
2938 let zone_name = interner.resolve(*name);
2939 let body_var = format!("e_{}", *counter);
2940 *counter += 1;
2941 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2942 for stmt in body.iter() {
2943 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2944 if !sv.is_empty() {
2945 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2946 }
2947 }
2948 output.push_str(&format!(
2949 "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
2950 var, zone_name, body_var
2951 ));
2952 }
2953 Stmt::LetPeerAgent { var: pa_var, address } => {
2954 let addr_var = encode_expr_src(address, counter, output, interner, variants);
2955 let pa_name = interner.resolve(*pa_var);
2956 output.push_str(&format!(
2957 "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
2958 var, addr_var, pa_name
2959 ));
2960 }
2961 _ => {
2962 return String::new();
2963 }
2964 }
2965
2966 var
2967}
2968
2969fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
2970 match stmt {
2971 Stmt::Inspect { target, arms, .. } => {
2972 let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
2973 let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
2974
2975 for arm in arms {
2976 if arm.variant.is_none() {
2977 otherwise_stmts = arm.body.iter().collect();
2978 } else {
2979 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
2980 variant_arms.push((arm, body_refs));
2981 }
2982 }
2983
2984 if variant_arms.is_empty() {
2985 let mut result = Vec::new();
2986 for s in &otherwise_stmts {
2987 for v in encode_stmts_src(s, counter, output, interner, variants) {
2988 result.push(v);
2989 }
2990 }
2991 return result;
2992 }
2993
2994 let has_otherwise = !otherwise_stmts.is_empty();
2999 let mut result = Vec::new();
3000
3001 let matched_var_name = if has_otherwise {
3003 let name = format!("__inspectMatched_{}", *counter);
3004 *counter += 1;
3005 let false_expr = format!("e_{}", *counter);
3006 *counter += 1;
3007 output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
3008 let let_stmt = format!("s_{}", *counter);
3009 *counter += 1;
3010 output.push_str(&format!(
3011 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3012 let_stmt, name, false_expr
3013 ));
3014 result.push(let_stmt);
3015 Some(name)
3016 } else {
3017 None
3018 };
3019
3020 for (arm, body_stmts) in &variant_arms {
3022 let variant_name = interner.resolve(arm.variant.unwrap());
3023
3024 let tag_target = encode_expr_src(target, counter, output, interner, variants);
3026 let tag_key = format!("e_{}", *counter);
3027 *counter += 1;
3028 output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
3029 let tag_get = format!("e_{}", *counter);
3030 *counter += 1;
3031 output.push_str(&format!(
3032 "Let {} be a new CMapGet with target {} and key {}.\n",
3033 tag_get, tag_target, tag_key
3034 ));
3035 let variant_text = format!("e_{}", *counter);
3036 *counter += 1;
3037 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
3038 let cond_var = format!("e_{}", *counter);
3039 *counter += 1;
3040 output.push_str(&format!(
3041 "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
3042 cond_var, tag_get, variant_text
3043 ));
3044
3045 let then_list = format!("stmtList_{}", *counter);
3047 *counter += 1;
3048 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
3049
3050 if let Some(ref mname) = matched_var_name {
3052 let true_expr = format!("e_{}", *counter);
3053 *counter += 1;
3054 output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
3055 let set_stmt = format!("s_{}", *counter);
3056 *counter += 1;
3057 output.push_str(&format!(
3058 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3059 set_stmt, mname, true_expr
3060 ));
3061 output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
3062 }
3063
3064 for (field_name, binding_name) in &arm.bindings {
3066 let field_str = interner.resolve(*field_name);
3067 let bind_str = interner.resolve(*binding_name);
3068 let bind_target = encode_expr_src(target, counter, output, interner, variants);
3069 let fkey = format!("e_{}", *counter);
3070 *counter += 1;
3071 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
3072 let fget = format!("e_{}", *counter);
3073 *counter += 1;
3074 output.push_str(&format!(
3075 "Let {} be a new CMapGet with target {} and key {}.\n",
3076 fget, bind_target, fkey
3077 ));
3078 let bind_let = format!("s_{}", *counter);
3079 *counter += 1;
3080 output.push_str(&format!(
3081 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3082 bind_let, bind_str, fget
3083 ));
3084 output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
3085 }
3086
3087 for body_stmt in body_stmts {
3089 match body_stmt {
3090 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
3091 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
3092 for v in vars {
3093 output.push_str(&format!("Push {} to {}.\n", v, then_list));
3094 }
3095 }
3096 _ => {
3097 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
3098 if !bvar.is_empty() {
3099 output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
3100 }
3101 }
3102 }
3103 }
3104
3105 let empty_else = format!("stmtList_{}", *counter);
3107 *counter += 1;
3108 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3109
3110 let if_var = format!("s_{}", *counter);
3112 *counter += 1;
3113 output.push_str(&format!(
3114 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3115 if_var, cond_var, then_list, empty_else
3116 ));
3117
3118 result.push(if_var);
3119 }
3120
3121 if let Some(ref mname) = matched_var_name {
3123 let matched_ref = format!("e_{}", *counter);
3124 *counter += 1;
3125 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
3126 let not_matched = format!("e_{}", *counter);
3127 *counter += 1;
3128 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
3129
3130 let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
3131 let empty_else = format!("stmtList_{}", *counter);
3132 *counter += 1;
3133 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3134
3135 let otherwise_if = format!("s_{}", *counter);
3136 *counter += 1;
3137 output.push_str(&format!(
3138 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3139 otherwise_if, not_matched, otherwise_block, empty_else
3140 ));
3141 result.push(otherwise_if);
3142 }
3143
3144 result
3145 }
3146 Stmt::Repeat { pattern, iterable, body, .. } => {
3147 let loop_var_name = match pattern {
3149 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3150 Pattern::Tuple(syms) => {
3151 if let Some(s) = syms.first() {
3152 interner.resolve(*s).to_string()
3153 } else {
3154 "item".to_string()
3155 }
3156 }
3157 };
3158
3159 let idx_name = format!("__idx_{}", *counter);
3161 *counter += 1;
3162
3163 let idx_init_expr = format!("e_{}", *counter);
3165 *counter += 1;
3166 output.push_str(&format!("Let {} be a new CInt with value 1.\n", idx_init_expr));
3167 let idx_init = format!("s_{}", *counter);
3168 *counter += 1;
3169 output.push_str(&format!(
3170 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3171 idx_init, idx_name, idx_init_expr
3172 ));
3173
3174 let iter_for_len = encode_expr_src(iterable, counter, output, interner, variants);
3176 let idx_var_expr = format!("e_{}", *counter);
3177 *counter += 1;
3178 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_var_expr, idx_name));
3179 let len_expr = format!("e_{}", *counter);
3180 *counter += 1;
3181 output.push_str(&format!("Let {} be a new CLen with target {}.\n", len_expr, iter_for_len));
3182 let while_cond = format!("e_{}", *counter);
3183 *counter += 1;
3184 output.push_str(&format!(
3185 "Let {} be a new CBinOp with op \"<=\" and left {} and right {}.\n",
3186 while_cond, idx_var_expr, len_expr
3187 ));
3188
3189 let while_body_list = format!("stmtList_{}", *counter);
3191 *counter += 1;
3192 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", while_body_list));
3193
3194 let iter_for_index = encode_expr_src(iterable, counter, output, interner, variants);
3196 let idx_ref = format!("e_{}", *counter);
3197 *counter += 1;
3198 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref, idx_name));
3199 let elem_expr = format!("e_{}", *counter);
3200 *counter += 1;
3201 output.push_str(&format!(
3202 "Let {} be a new CIndex with coll {} and idx {}.\n",
3203 elem_expr, iter_for_index, idx_ref
3204 ));
3205 let elem_let = format!("s_{}", *counter);
3206 *counter += 1;
3207 output.push_str(&format!(
3208 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3209 elem_let, loop_var_name, elem_expr
3210 ));
3211 output.push_str(&format!("Push {} to {}.\n", elem_let, while_body_list));
3212
3213 let body_refs: Vec<&Stmt> = body.iter().collect();
3215 for body_stmt in &body_refs {
3216 match body_stmt {
3217 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
3218 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
3219 for v in vars {
3220 output.push_str(&format!("Push {} to {}.\n", v, while_body_list));
3221 }
3222 }
3223 _ => {
3224 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
3225 if !bvar.is_empty() {
3226 output.push_str(&format!("Push {} to {}.\n", bvar, while_body_list));
3227 }
3228 }
3229 }
3230 }
3231
3232 let idx_ref2 = format!("e_{}", *counter);
3234 *counter += 1;
3235 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref2, idx_name));
3236 let one_expr = format!("e_{}", *counter);
3237 *counter += 1;
3238 output.push_str(&format!("Let {} be a new CInt with value 1.\n", one_expr));
3239 let inc_expr = format!("e_{}", *counter);
3240 *counter += 1;
3241 output.push_str(&format!(
3242 "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
3243 inc_expr, idx_ref2, one_expr
3244 ));
3245 let inc_set = format!("s_{}", *counter);
3246 *counter += 1;
3247 output.push_str(&format!(
3248 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3249 inc_set, idx_name, inc_expr
3250 ));
3251 output.push_str(&format!("Push {} to {}.\n", inc_set, while_body_list));
3252
3253 let while_var = format!("s_{}", *counter);
3255 *counter += 1;
3256 output.push_str(&format!(
3257 "Let {} be a new CWhile with cond {} and body {}.\n",
3258 while_var, while_cond, while_body_list
3259 ));
3260
3261 vec![idx_init, while_var]
3262 }
3263 _ => {
3264 let v = encode_stmt_src(stmt, counter, output, interner, variants);
3265 if v.is_empty() {
3266 vec![]
3267 } else {
3268 vec![v]
3269 }
3270 }
3271 }
3272}
3273
3274fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3275 let list_var = format!("stmtList_{}", *counter);
3276 *counter += 1;
3277 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
3278
3279 for stmt in stmts {
3280 for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
3281 output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
3282 }
3283 }
3284
3285 list_var
3286}
3287
3288fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
3289 match expr {
3290 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3291 _ => "unknown".to_string(),
3292 }
3293}
3294
3295pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
3308 let full_source = if program.contains("## Main") || program.contains("## To ") {
3309 program.to_string()
3310 } else {
3311 format!("## Main\n{}", program)
3312 };
3313
3314 let mut interner = Interner::new();
3315 let mut lexer = Lexer::new(&full_source, &mut interner);
3316 let tokens = lexer.tokenize();
3317
3318 let type_registry = {
3319 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3320 let result = discovery.run_full();
3321 result.types
3322 };
3323
3324 let mut world_state = WorldState::new();
3325 let expr_arena = Arena::new();
3326 let term_arena = Arena::new();
3327 let np_arena = Arena::new();
3328 let sym_arena = Arena::new();
3329 let role_arena = Arena::new();
3330 let pp_arena = Arena::new();
3331 let stmt_arena: Arena<Stmt> = Arena::new();
3332 let imperative_expr_arena: Arena<Expr> = Arena::new();
3333 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3334
3335 let ast_ctx = AstContext::with_types(
3336 &expr_arena, &term_arena, &np_arena, &sym_arena,
3337 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3338 &type_expr_arena,
3339 );
3340
3341 let mut parser = crate::parser::Parser::new(
3342 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3343 );
3344 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3345
3346 let optimized = crate::optimize::optimize_for_projection(
3351 stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
3352 );
3353
3354 let mut output = String::new();
3355
3356 for stmt in &optimized {
3357 if matches!(stmt, Stmt::FunctionDef { .. }) {
3358 decompile_stmt(stmt, &interner, &mut output, 0);
3359 output.push('\n');
3360 }
3361 }
3362
3363 output.push_str("## Main\n");
3364 for stmt in &optimized {
3365 if !matches!(stmt, Stmt::FunctionDef { .. }) {
3366 decompile_stmt(stmt, &interner, &mut output, 0);
3367 }
3368 }
3369
3370 Ok(output)
3371}
3372
3373fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
3374 let pad = " ".repeat(indent);
3375 match stmt {
3376 Stmt::FunctionDef { name, params, body, return_type, .. } => {
3377 let fn_name = interner.resolve(*name);
3378 let param_strs: Vec<String> = params
3379 .iter()
3380 .map(|(name, ty)| {
3381 let pname = interner.resolve(*name);
3382 format!("{}: {}", pname, decompile_type_expr(ty, interner))
3383 })
3384 .collect();
3385 let ret_str = if let Some(rt) = return_type {
3386 format!(" -> {}", decompile_type_expr(rt, interner))
3387 } else {
3388 String::new()
3389 };
3390 out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
3391 for s in body.iter() {
3392 decompile_stmt(s, interner, out, indent + 1);
3393 }
3394 }
3395 Stmt::Let { var, value, mutable, .. } => {
3396 let name = interner.resolve(*var);
3397 let expr_str = decompile_expr(value, interner);
3398 if *mutable {
3399 out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
3400 } else {
3401 out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
3402 }
3403 }
3404 Stmt::Set { target, value } => {
3405 let name = interner.resolve(*target);
3406 let expr_str = decompile_expr(value, interner);
3407 out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
3408 }
3409 Stmt::Show { object, .. } => {
3410 let expr_str = decompile_expr(object, interner);
3411 out.push_str(&format!("{}Show {}.\n", pad, expr_str));
3412 }
3413 Stmt::Return { value } => {
3414 if let Some(expr) = value {
3415 let expr_str = decompile_expr(expr, interner);
3416 out.push_str(&format!("{}Return {}.\n", pad, expr_str));
3417 } else {
3418 out.push_str(&format!("{}Return.\n", pad));
3419 }
3420 }
3421 Stmt::If { cond, then_block, else_block } => {
3422 let cond_str = decompile_expr(cond, interner);
3423 out.push_str(&format!("{}If {}:\n", pad, cond_str));
3424 for s in then_block.iter() {
3425 decompile_stmt(s, interner, out, indent + 1);
3426 }
3427 if let Some(els) = else_block {
3428 out.push_str(&format!("{}Otherwise:\n", pad));
3429 for s in els.iter() {
3430 decompile_stmt(s, interner, out, indent + 1);
3431 }
3432 }
3433 }
3434 Stmt::While { cond, body, .. } => {
3435 let cond_str = decompile_expr(cond, interner);
3436 out.push_str(&format!("{}While {}:\n", pad, cond_str));
3437 for s in body.iter() {
3438 decompile_stmt(s, interner, out, indent + 1);
3439 }
3440 }
3441 Stmt::Call { function, args } => {
3442 let fn_name = interner.resolve(*function);
3443 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3444 if arg_strs.is_empty() {
3445 out.push_str(&format!("{}{}().\n", pad, fn_name));
3446 } else {
3447 out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
3448 }
3449 }
3450 Stmt::Push { value, collection } => {
3451 let val_str = decompile_expr(value, interner);
3452 let coll_str = decompile_expr(collection, interner);
3453 out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
3454 }
3455 Stmt::SetIndex { collection, index, value } => {
3456 let coll_str = decompile_expr(collection, interner);
3457 let idx_str = decompile_expr(index, interner);
3458 let val_str = decompile_expr(value, interner);
3459 out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
3460 }
3461 Stmt::SetField { object, field, value } => {
3462 let obj_str = decompile_expr(object, interner);
3463 let field_name = interner.resolve(*field);
3464 let val_str = decompile_expr(value, interner);
3465 out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
3466 }
3467 Stmt::Repeat { pattern, iterable, body, .. } => {
3468 let var_name = match pattern {
3469 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3470 Pattern::Tuple(syms) => {
3471 syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
3472 }
3473 };
3474 let iter_str = decompile_expr(iterable, interner);
3475 out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
3476 for s in body.iter() {
3477 decompile_stmt(s, interner, out, indent + 1);
3478 }
3479 }
3480 Stmt::Inspect { target, arms, .. } => {
3481 let target_str = decompile_expr(target, interner);
3482 out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
3483 for arm in arms {
3484 if let Some(variant) = arm.variant {
3485 let variant_name = interner.resolve(variant);
3486 let bindings: Vec<String> = arm.bindings.iter()
3487 .map(|(_, b)| interner.resolve(*b).to_string())
3488 .collect();
3489 if bindings.is_empty() {
3490 out.push_str(&format!("{} When {}:\n", pad, variant_name));
3491 } else {
3492 out.push_str(&format!("{} When {}({}):\n", pad, variant_name, bindings.join(", ")));
3493 }
3494 } else {
3495 out.push_str(&format!("{} Otherwise:\n", pad));
3496 }
3497 for s in arm.body.iter() {
3498 decompile_stmt(s, interner, out, indent + 2);
3499 }
3500 }
3501 }
3502 Stmt::Pop { collection, into } => {
3503 let coll_str = decompile_expr(collection, interner);
3504 if let Some(target) = into {
3505 let target_name = interner.resolve(*target);
3506 out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
3507 } else {
3508 out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
3509 }
3510 }
3511 Stmt::Break => {
3512 out.push_str(&format!("{}Break.\n", pad));
3513 }
3514 Stmt::RuntimeAssert { condition } => {
3515 let cond_str = decompile_expr(condition, interner);
3516 out.push_str(&format!("{}Assert that {}.\n", pad, cond_str));
3517 }
3518 Stmt::Add { value, collection } => {
3519 let val_str = decompile_expr(value, interner);
3520 let coll_str = decompile_expr(collection, interner);
3521 out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
3522 }
3523 Stmt::Remove { value, collection } => {
3524 let val_str = decompile_expr(value, interner);
3525 let coll_str = decompile_expr(collection, interner);
3526 out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
3527 }
3528 Stmt::Zone { name, body, .. } => {
3529 let zone_name = interner.resolve(*name);
3530 out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
3531 for s in body.iter() {
3532 decompile_stmt(s, interner, out, indent + 1);
3533 }
3534 }
3535 Stmt::ReadFrom { var, .. } => {
3536 let var_name = interner.resolve(*var);
3537 out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
3538 }
3539 Stmt::WriteFile { content, path } => {
3540 let content_str = decompile_expr(content, interner);
3541 let path_str = decompile_expr(path, interner);
3542 out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
3543 }
3544 Stmt::Sleep { milliseconds } => {
3545 let ms = decompile_expr(milliseconds, interner);
3546 out.push_str(&format!("{}Sleep {}.\n", pad, ms));
3547 }
3548 _ => {
3549 }
3552 }
3553}
3554
3555fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
3556 match expr {
3557 Expr::Literal(lit) => match lit {
3558 Literal::Number(n) => n.to_string(),
3559 Literal::Float(f) => format!("{}", f),
3560 Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
3561 Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
3562 Literal::Nothing => "nothing".to_string(),
3563 Literal::Char(c) => format!("'{}'", c),
3564 Literal::Duration(ns) => format!("{}", ns),
3565 Literal::Date(days) => format!("{}", days),
3566 Literal::Moment(ns) => format!("{}", ns),
3567 Literal::Span { months, days } => format!("{} months {} days", months, days),
3568 Literal::Time(ns) => format!("{}", ns),
3569 },
3570 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3571 Expr::BinaryOp { op, left, right } => {
3572 let l = if matches!(left, Expr::BinaryOp { .. }) {
3573 format!("({})", decompile_expr(left, interner))
3574 } else {
3575 decompile_expr(left, interner)
3576 };
3577 let r = if matches!(right, Expr::BinaryOp { .. }) {
3578 format!("({})", decompile_expr(right, interner))
3579 } else {
3580 decompile_expr(right, interner)
3581 };
3582 if matches!(op, BinaryOpKind::Shl) {
3585 if let Expr::Literal(Literal::Number(k)) = right {
3587 let multiplier = 1i64 << k;
3588 return format!("{} * {}", l, multiplier);
3589 }
3590 }
3591 if matches!(op, BinaryOpKind::Shr) {
3592 if let Expr::Literal(Literal::Number(k)) = right {
3594 let divisor = 1i64 << k;
3595 return format!("{} / {}", l, divisor);
3596 }
3597 }
3598 let op_str = match op {
3599 BinaryOpKind::Add => "+",
3600 BinaryOpKind::Subtract => "-",
3601 BinaryOpKind::Multiply => "*",
3602 BinaryOpKind::Divide => "/",
3603 BinaryOpKind::Modulo => "%",
3604 BinaryOpKind::Eq => "equals",
3605 BinaryOpKind::NotEq => "is not",
3606 BinaryOpKind::Lt => "is less than",
3607 BinaryOpKind::Gt => "is greater than",
3608 BinaryOpKind::LtEq => "is at most",
3609 BinaryOpKind::GtEq => "is at least",
3610 BinaryOpKind::And => "and",
3611 BinaryOpKind::Or => "or",
3612 BinaryOpKind::Concat => "+",
3613 BinaryOpKind::BitXor => "+",
3614 BinaryOpKind::Shl => "*",
3615 BinaryOpKind::Shr => "/",
3616 };
3617 format!("{} {} {}", l, op_str, r)
3618 }
3619 Expr::Not { operand } => {
3620 let inner = decompile_expr(operand, interner);
3621 format!("not {}", inner)
3622 }
3623 Expr::Call { function, args } => {
3624 let fn_name = interner.resolve(*function);
3625 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3626 if arg_strs.is_empty() {
3627 format!("{}()", fn_name)
3628 } else {
3629 format!("{}({})", fn_name, arg_strs.join(", "))
3630 }
3631 }
3632 Expr::Index { collection, index } => {
3633 let coll = decompile_expr(collection, interner);
3634 let idx = decompile_expr(index, interner);
3635 format!("item {} of {}", idx, coll)
3636 }
3637 Expr::Length { collection } => {
3638 let coll = decompile_expr(collection, interner);
3639 format!("length of {}", coll)
3640 }
3641 Expr::FieldAccess { object, field } => {
3642 let obj = decompile_expr(object, interner);
3643 let field_name = interner.resolve(*field);
3644 format!("{} of {}", field_name, obj)
3645 }
3646 Expr::New { type_name, .. } => {
3647 let tn = interner.resolve(*type_name);
3648 format!("a new {}", tn)
3649 }
3650 Expr::NewVariant { variant, fields, .. } => {
3651 let vn = interner.resolve(*variant);
3652 if fields.is_empty() {
3653 format!("a new {}", vn)
3654 } else {
3655 let parts: Vec<String> = fields.iter().map(|(name, val)| {
3656 let n = interner.resolve(*name);
3657 let v = decompile_expr(val, interner);
3658 format!("{} {}", n, v)
3659 }).collect();
3660 format!("a new {} with {}", vn, parts.join(" and "))
3661 }
3662 }
3663 Expr::InterpolatedString(parts) => {
3664 let mut result = String::new();
3665 for part in parts {
3666 match part {
3667 StringPart::Literal(sym) => {
3668 result.push_str(&interner.resolve(*sym));
3669 }
3670 StringPart::Expr { value, debug, .. } => {
3671 let expr_str = decompile_expr(value, interner);
3672 if *debug {
3673 result.push_str(&format!("{{{}=}}", expr_str));
3674 } else {
3675 result.push_str(&format!("{{{}}}", expr_str));
3676 }
3677 }
3678 }
3679 }
3680 format!("\"{}\"", result)
3681 }
3682 Expr::Slice { collection, start, end } => {
3683 let coll = decompile_expr(collection, interner);
3684 let s = decompile_expr(start, interner);
3685 let e = decompile_expr(end, interner);
3686 format!("{} {} through {}", coll, s, e)
3687 }
3688 Expr::Copy { expr } => {
3689 let inner = decompile_expr(expr, interner);
3690 format!("copy of {}", inner)
3691 }
3692 Expr::Give { value } => {
3693 let inner = decompile_expr(value, interner);
3694 format!("Give {}", inner)
3695 }
3696 Expr::Contains { collection, value } => {
3697 let coll = decompile_expr(collection, interner);
3698 let val = decompile_expr(value, interner);
3699 format!("{} contains {}", coll, val)
3700 }
3701 Expr::Union { left, right } => {
3702 let l = decompile_expr(left, interner);
3703 let r = decompile_expr(right, interner);
3704 format!("{} union {}", l, r)
3705 }
3706 Expr::Intersection { left, right } => {
3707 let l = decompile_expr(left, interner);
3708 let r = decompile_expr(right, interner);
3709 format!("{} intersection {}", l, r)
3710 }
3711 Expr::List(elems) => {
3712 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3713 format!("[{}]", parts.join(", "))
3714 }
3715 Expr::Tuple(elems) => {
3716 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3717 format!("({})", parts.join(", "))
3718 }
3719 Expr::Range { start, end } => {
3720 let s = decompile_expr(start, interner);
3721 let e = decompile_expr(end, interner);
3722 format!("{} to {}", s, e)
3723 }
3724 Expr::OptionSome { value } => {
3725 let inner = decompile_expr(value, interner);
3726 format!("some {}", inner)
3727 }
3728 Expr::OptionNone => "none".to_string(),
3729 Expr::WithCapacity { value, capacity } => {
3730 let val = decompile_expr(value, interner);
3731 let cap = decompile_expr(capacity, interner);
3732 format!("{} with capacity {}", val, cap)
3733 }
3734 Expr::Escape { language, code } => {
3735 let lang = interner.resolve(*language);
3736 let src = interner.resolve(*code);
3737 format!("Escape to {}:\n{}", lang, src)
3738 }
3739 Expr::ManifestOf { zone } => {
3740 let z = decompile_expr(zone, interner);
3741 format!("the manifest of {}", z)
3742 }
3743 Expr::ChunkAt { index, zone } => {
3744 let idx = decompile_expr(index, interner);
3745 let z = decompile_expr(zone, interner);
3746 format!("the chunk at {} in {}", idx, z)
3747 }
3748 Expr::Closure { params, body, return_type } => {
3749 let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
3750 let n = interner.resolve(*name);
3751 let t = decompile_type_expr(ty, interner);
3752 format!("{}: {}", n, t)
3753 }).collect();
3754 let ret = if let Some(rt) = return_type {
3755 format!(" -> {}", decompile_type_expr(rt, interner))
3756 } else {
3757 String::new()
3758 };
3759 match body {
3760 ClosureBody::Expression(expr) => {
3761 let e = decompile_expr(expr, interner);
3762 format!("({}){} -> {}", param_strs.join(", "), ret, e)
3763 }
3764 ClosureBody::Block(_) => {
3765 format!("({}){} -> [block]", param_strs.join(", "), ret)
3766 }
3767 }
3768 }
3769 Expr::CallExpr { callee, args } => {
3770 let c = decompile_expr(callee, interner);
3771 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3772 format!("{}({})", c, arg_strs.join(", "))
3773 }
3774 }
3775}
3776
3777fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
3778 match ty {
3779 TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
3780 TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
3781 TypeExpr::Generic { base, params } => {
3782 let base_str = interner.resolve(*base);
3783 let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
3784 format!("{} of {}", base_str, param_strs.join(" and "))
3785 }
3786 TypeExpr::Function { inputs, output } => {
3787 let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
3788 let out_str = decompile_type_expr(output, interner);
3789 format!("fn({}) -> {}", in_strs.join(", "), out_str)
3790 }
3791 TypeExpr::Refinement { base, .. } => {
3792 decompile_type_expr(base, interner)
3793 }
3794 TypeExpr::Persistent { inner } => {
3795 format!("Persistent {}", decompile_type_expr(inner, interner))
3796 }
3797 }
3798}
3799
3800pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
3807 let mut interner = Interner::new();
3808 let mut lexer = Lexer::new(source, &mut interner);
3809 let tokens = lexer.tokenize();
3810
3811 let type_registry = {
3812 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3813 let result = discovery.run_full();
3814 result.types
3815 };
3816
3817 let mut world_state = WorldState::new();
3818 let expr_arena = Arena::new();
3819 let term_arena = Arena::new();
3820 let np_arena = Arena::new();
3821 let sym_arena = Arena::new();
3822 let role_arena = Arena::new();
3823 let pp_arena = Arena::new();
3824 let stmt_arena: Arena<Stmt> = Arena::new();
3825 let imperative_expr_arena: Arena<Expr> = Arena::new();
3826 let type_expr_arena: Arena<TypeExpr> = Arena::new();
3827
3828 let ast_ctx = AstContext::with_types(
3829 &expr_arena, &term_arena, &np_arena, &sym_arena,
3830 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3831 &type_expr_arena,
3832 );
3833
3834 let mut parser = crate::parser::Parser::new(
3835 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3836 );
3837 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3838
3839 verify_no_overhead_stmts(&stmts, &interner)
3840}
3841
3842const CORE_VARIANT_NAMES: &[&str] = &[
3843 "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
3844 "CCall", "CIndex", "CLen", "CMapGet",
3845 "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
3846 "CCallS", "CPush", "CSetIdx", "CMapSet",
3847 "CFuncDef", "CProg",
3848 "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
3849];
3850
3851fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
3852 for stmt in stmts {
3853 check_stmt_overhead(stmt, interner)?;
3854 }
3855 Ok(())
3856}
3857
3858fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
3859 match stmt {
3860 Stmt::Inspect { arms, .. } => {
3861 for arm in arms {
3862 if let Some(variant) = arm.variant {
3863 let variant_name = interner.resolve(variant);
3864 if CORE_VARIANT_NAMES.contains(&variant_name) {
3865 return Err(format!(
3866 "Interpretive overhead: Inspect dispatches on Core variant '{}'",
3867 variant_name
3868 ));
3869 }
3870 }
3871 for s in arm.body.iter() {
3872 check_stmt_overhead(s, interner)?;
3873 }
3874 }
3875 }
3876 Stmt::If { cond, then_block, else_block } => {
3877 check_expr_overhead(cond, interner)?;
3878 for s in then_block.iter() {
3879 check_stmt_overhead(s, interner)?;
3880 }
3881 if let Some(els) = else_block {
3882 for s in els.iter() {
3883 check_stmt_overhead(s, interner)?;
3884 }
3885 }
3886 }
3887 Stmt::While { cond, body, .. } => {
3888 check_expr_overhead(cond, interner)?;
3889 for s in body.iter() {
3890 check_stmt_overhead(s, interner)?;
3891 }
3892 }
3893 Stmt::FunctionDef { body, .. } => {
3894 for s in body.iter() {
3895 check_stmt_overhead(s, interner)?;
3896 }
3897 }
3898 Stmt::Repeat { body, .. } => {
3899 for s in body.iter() {
3900 check_stmt_overhead(s, interner)?;
3901 }
3902 }
3903 Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
3904 check_expr_overhead(value, interner)?;
3905 }
3906 Stmt::Return { value } => {
3907 if let Some(v) = value {
3908 check_expr_overhead(v, interner)?;
3909 }
3910 }
3911 _ => {}
3912 }
3913 Ok(())
3914}
3915
3916fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
3917 match expr {
3918 Expr::Index { collection, index } => {
3919 if let Expr::Identifier(coll_sym) = collection {
3921 let coll_name = interner.resolve(*coll_sym);
3922 if coll_name == "env" {
3923 if let Expr::Literal(Literal::Text(_)) = index {
3924 return Err(
3925 "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
3926 );
3927 }
3928 }
3929 }
3930 check_expr_overhead(collection, interner)?;
3931 check_expr_overhead(index, interner)?;
3932 }
3933 Expr::New { type_name, .. } => {
3934 let tn = interner.resolve(*type_name);
3935 if CORE_VARIANT_NAMES.contains(&tn) {
3936 return Err(format!(
3937 "Interpretive overhead: Core type constructor 'new {}'", tn
3938 ));
3939 }
3940 }
3941 Expr::NewVariant { variant, .. } => {
3942 let vn = interner.resolve(*variant);
3943 if CORE_VARIANT_NAMES.contains(&vn) {
3944 return Err(format!(
3945 "Interpretive overhead: Core variant constructor '{}'", vn
3946 ));
3947 }
3948 }
3949 Expr::Call { function, args } => {
3950 let fn_name = interner.resolve(*function);
3951 if CORE_VARIANT_NAMES.contains(&fn_name) {
3952 return Err(format!(
3953 "Interpretive overhead: Core variant call '{}'", fn_name
3954 ));
3955 }
3956 for a in args {
3957 check_expr_overhead(a, interner)?;
3958 }
3959 }
3960 Expr::BinaryOp { left, right, .. } => {
3961 check_expr_overhead(left, interner)?;
3962 check_expr_overhead(right, interner)?;
3963 }
3964 Expr::Not { operand } => {
3965 check_expr_overhead(operand, interner)?;
3966 }
3967 Expr::Length { collection } => {
3968 check_expr_overhead(collection, interner)?;
3969 }
3970 Expr::FieldAccess { object, .. } => {
3971 check_expr_overhead(object, interner)?;
3972 }
3973 _ => {}
3974 }
3975 Ok(())
3976}
3977
3978pub fn pe_source_text() -> &'static str {
3984 include_str!("optimize/pe_source.logos")
3985}
3986
3987pub fn decompile_source_text() -> &'static str {
3988 include_str!("optimize/decompile_source.logos")
3989}
3990
3991pub fn pe_bti_source_text() -> &'static str {
3992 include_str!("optimize/pe_bti_source.logos")
3993}
3994
3995pub fn pe_mini_source_text() -> &'static str {
3996 include_str!("optimize/pe_mini_source.logos")
3997}
3998
3999const CORE_TYPES_FOR_PE: &str = r#"
4000## A CExpr is one of:
4001 A CInt with value Int.
4002 A CFloat with value Real.
4003 A CBool with value Bool.
4004 A CText with value Text.
4005 A CVar with name Text.
4006 A CBinOp with op Text and left CExpr and right CExpr.
4007 A CNot with inner CExpr.
4008 A CCall with name Text and args Seq of CExpr.
4009 A CIndex with coll CExpr and idx CExpr.
4010 A CLen with target CExpr.
4011 A CMapGet with target CExpr and key CExpr.
4012 A CNewSeq.
4013 A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
4014 A CList with items Seq of CExpr.
4015 A CRange with start CExpr and end CExpr.
4016 A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
4017 A CCopy with target CExpr.
4018 A CNewSet.
4019 A CContains with coll CExpr and elem CExpr.
4020 A CUnion with left CExpr and right CExpr.
4021 A CIntersection with left CExpr and right CExpr.
4022 A COptionSome with inner CExpr.
4023 A COptionNone.
4024 A CTuple with items Seq of CExpr.
4025 A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
4026 A CFieldAccess with target CExpr and field Text.
4027 A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
4028 A CCallExpr with target CExpr and args Seq of CExpr.
4029 A CInterpolatedString with parts Seq of CStringPart.
4030 A CDuration with amount CExpr and unit Text.
4031 A CTimeNow.
4032 A CDateToday.
4033 A CEscExpr with code Text.
4034
4035## A CStringPart is one of:
4036 A CLiteralPart with value Text.
4037 A CExprPart with expr CExpr.
4038
4039## A CStmt is one of:
4040 A CLet with name Text and expr CExpr.
4041 A CSet with name Text and expr CExpr.
4042 A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
4043 A CWhile with cond CExpr and body Seq of CStmt.
4044 A CReturn with expr CExpr.
4045 A CShow with expr CExpr.
4046 A CCallS with name Text and args Seq of CExpr.
4047 A CPush with expr CExpr and target Text.
4048 A CSetIdx with target Text and idx CExpr and val CExpr.
4049 A CMapSet with target Text and key CExpr and val CExpr.
4050 A CPop with target Text.
4051 A CRepeat with var Text and coll CExpr and body Seq of CStmt.
4052 A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
4053 A CBreak.
4054 A CAdd with elem CExpr and target Text.
4055 A CRemove with elem CExpr and target Text.
4056 A CSetField with target Text and field Text and val CExpr.
4057 A CStructDef with name Text and fieldNames Seq of Text.
4058 A CInspect with target CExpr and arms Seq of CMatchArm.
4059 A CEnumDef with name Text and variants Seq of Text.
4060 A CRuntimeAssert with cond CExpr and msg CExpr.
4061 A CGive with expr CExpr and target Text.
4062 A CEscStmt with code Text.
4063 A CSleep with duration CExpr.
4064 A CReadConsole with target Text.
4065 A CReadFile with path CExpr and target Text.
4066 A CWriteFile with path CExpr and content CExpr.
4067 A CCheck with predicate CExpr and msg CExpr.
4068 A CAssert with proposition CExpr.
4069 A CTrust with proposition CExpr and justification Text.
4070 A CRequire with dependency Text.
4071 A CMerge with target Text and other CExpr.
4072 A CIncrease with target Text and amount CExpr.
4073 A CDecrease with target Text and amount CExpr.
4074 A CAppendToSeq with target Text and value CExpr.
4075 A CResolve with target Text.
4076 A CSync with target Text and channel CExpr.
4077 A CMount with target Text and path CExpr.
4078 A CConcurrent with branches Seq of Seq of CStmt.
4079 A CParallel with branches Seq of Seq of CStmt.
4080 A CLaunchTask with body Seq of CStmt and handle Text.
4081 A CStopTask with handle CExpr.
4082 A CSelect with branches Seq of CSelectBranch.
4083 A CCreatePipe with name Text and capacity CExpr.
4084 A CSendPipe with chan Text and value CExpr.
4085 A CReceivePipe with chan Text and target Text.
4086 A CTrySendPipe with chan Text and value CExpr.
4087 A CTryReceivePipe with chan Text and target Text.
4088 A CSpawn with agentType Text and target Text.
4089 A CSendMessage with target CExpr and msg CExpr.
4090 A CAwaitMessage with target Text.
4091 A CListen with addr CExpr and handler Text.
4092 A CConnectTo with addr CExpr and target Text.
4093 A CZone with name Text and kind Text and body Seq of CStmt.
4094
4095## A CSelectBranch is one of:
4096 A CSelectRecv with chan Text and var Text and body Seq of CStmt.
4097 A CSelectTimeout with duration CExpr and body Seq of CStmt.
4098
4099## A CMatchArm is one of:
4100 A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
4101 A COtherwise with body Seq of CStmt.
4102
4103## A CFunc is one of:
4104 A CFuncDef with name Text and params Seq of Text and body Seq of CStmt.
4105
4106## A CProgram is one of:
4107 A CProg with funcs Seq of CFunc and main Seq of CStmt.
4108
4109## A PEState is one of:
4110 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.
4111
4112## A CVal is one of:
4113 A VInt with value Int.
4114 A VFloat with value Real.
4115 A VBool with value Bool.
4116 A VText with value Text.
4117 A VSeq with items Seq of CVal.
4118 A VMap with entries Map of Text to CVal.
4119 A VError with msg Text.
4120 A VNothing.
4121 A VSet with items Seq of CVal.
4122 A VOption with inner CVal and present Bool.
4123 A VTuple with items Seq of CVal.
4124 A VStruct with typeName Text and fields Map of Text to CVal.
4125 A VVariant with typeName Text and variantName Text and fields Seq of CVal.
4126 A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
4127 A VDuration with millis Int.
4128 A VDate with year Int and month Int and day Int.
4129 A VMoment with millis Int.
4130 A VSpan with startMillis Int and endMillis Int.
4131 A VTime with hour Int and minute Int and second Int.
4132 A VCrdt with kind Text and state Map of Text to CVal.
4133"#;
4134
4135pub fn quote_pe_source() -> Result<String, String> {
4143 let pe_source = pe_source_text();
4144 let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
4145 let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
4146 Ok(format!("{}\n{}", pe_source, encoded))
4147}
4148
4149pub fn projection2_source() -> Result<String, String> {
4163 let pe_source = pe_source_text();
4164
4165 let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
4166
4167 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
4168}
4169
4170pub fn projection3_source() -> Result<String, String> {
4185 let pe_source = pe_source_text();
4186
4187 let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
4188
4189 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
4190}
4191
4192pub fn run_logos_source(source: &str) -> Result<String, String> {
4197 let compile_output = compile_program_full(source)
4198 .map_err(|e| format!("Compilation failed: {:?}", e))?;
4199
4200 let temp_base = std::env::temp_dir().join("logos_run_source");
4202 std::fs::create_dir_all(&temp_base)
4203 .map_err(|e| format!("mkdir failed: {}", e))?;
4204
4205 let pkg_name = format!(
4206 "logos_run_{}_{}",
4207 std::process::id(),
4208 std::time::SystemTime::now()
4209 .duration_since(std::time::UNIX_EPOCH)
4210 .unwrap()
4211 .as_nanos()
4212 );
4213 let project_dir = temp_base.join(&pkg_name);
4214
4215 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
4217 let workspace_root = manifest_dir.parent().unwrap().parent().unwrap();
4218
4219 let cargo_toml = format!(
4220 r#"[package]
4221name = "{}"
4222version = "0.1.0"
4223edition = "2021"
4224
4225[dependencies]
4226logicaffeine-data = {{ path = "{}/crates/logicaffeine_data" }}
4227logicaffeine-system = {{ path = "{}/crates/logicaffeine_system", features = ["full"] }}
4228tokio = {{ version = "1", features = ["rt-multi-thread", "macros"] }}
4229serde = {{ version = "1", features = ["derive"] }}
4230rayon = "1"
4231"#,
4232 pkg_name,
4233 workspace_root.display(),
4234 workspace_root.display(),
4235 );
4236
4237 std::fs::create_dir_all(project_dir.join("src"))
4238 .map_err(|e| format!("mkdir failed: {}", e))?;
4239 std::fs::write(project_dir.join("Cargo.toml"), cargo_toml)
4240 .map_err(|e| format!("Write Cargo.toml failed: {}", e))?;
4241 std::fs::write(project_dir.join("src/main.rs"), &compile_output.rust_code)
4242 .map_err(|e| format!("Write main.rs failed: {}", e))?;
4243
4244 let target_dir = std::env::temp_dir().join("logos_e2e_cache");
4246 std::fs::create_dir_all(&target_dir)
4247 .map_err(|e| format!("mkdir target failed: {}", e))?;
4248
4249 let output = std::process::Command::new("cargo")
4250 .args(["run", "--quiet"])
4251 .current_dir(&project_dir)
4252 .env("CARGO_TARGET_DIR", &target_dir)
4253 .env("RUST_MIN_STACK", "268435456")
4254 .output()
4255 .map_err(|e| format!("cargo run failed: {}", e))?;
4256
4257 let _ = std::fs::remove_dir_all(&project_dir);
4259
4260 if !output.status.success() {
4261 return Err(format!(
4262 "Execution failed:\nstderr: {}\nstdout: {}",
4263 String::from_utf8_lossy(&output.stderr),
4264 String::from_utf8_lossy(&output.stdout),
4265 ));
4266 }
4267
4268 Ok(String::from_utf8_lossy(&output.stdout).to_string())
4269}
4270
4271pub fn projection1_source_real(core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
4276 let full_source = if program.contains("## Main") || program.contains("## To ") {
4277 program.to_string()
4278 } else {
4279 format!("## Main\n{}", program)
4280 };
4281
4282 let encoded = encode_program_source(&full_source)
4284 .map_err(|e| format!("Failed to encode program: {:?}", e))?;
4285
4286 let pe_source = pe_source_text();
4288 let decompile_source = decompile_source_text();
4289
4290 let actual_core_types = if core_types.is_empty() { CORE_TYPES_FOR_PE } else { core_types };
4292
4293 let driver = r#"
4294 Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4295 Let residual be peBlock(encodedMain, state).
4296 Let source be decompileBlock(residual, 0).
4297 Show source.
4298"#;
4299
4300 let combined = format!(
4301 "{}\n{}\n{}\n## Main\n{}\n{}",
4302 actual_core_types,
4303 pe_source,
4304 decompile_source,
4305 encoded,
4306 driver,
4307 );
4308
4309 let raw_residual = run_logos_source(&combined)?;
4311 let trimmed = raw_residual.trim();
4312
4313 if trimmed.is_empty() {
4315 return Ok("## Main\n".to_string());
4316 }
4317
4318 if trimmed.contains("## To ") {
4320 Ok(trimmed.to_string())
4321 } else {
4322 Ok(format!("## Main\n{}", trimmed))
4323 }
4324}
4325
4326pub fn projection2_source_real(_core_types: &str, _interpreter: &str) -> Result<String, String> {
4339 let pe_bti = pe_bti_source_text();
4340
4341 let compiler = replace_word(
4343 &replace_word(
4344 &replace_word(pe_bti, "peExprB", "compileExpr"),
4345 "peBlockB", "compileBlock",
4346 ),
4347 "extractReturnB", "extractReturn",
4348 );
4349
4350 Ok(compiler.to_string())
4351}
4352
4353pub fn genuine_projection2_residual() -> Result<String, String> {
4364 let pe_mini = pe_mini_source_text();
4365 let pe = pe_source_text();
4366 let decompile = decompile_source_text();
4367
4368 let program = format!(
4370 "{}\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",
4371 CORE_TYPES_FOR_PE, pe_mini
4372 );
4373
4374 let encoded = encode_program_source_compact(&program)
4376 .map_err(|e| format!("Failed to encode pe_mini: {:?}", e))?;
4377
4378 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4380 Let residual be peBlock(encodedMain, state).
4381 Let nl be chr(10).
4382 Let mutable output be "".
4383 Let specFuncs be peFuncs(state).
4384 Let specNames be collectCallNames(residual).
4385 Repeat for sn in specNames:
4386 Let snKey be "{sn}".
4387 If specFuncs contains snKey:
4388 Let fdef be item snKey of specFuncs.
4389 Let funcSrc be decompileFunc(fdef).
4390 If the length of funcSrc is greater than 0:
4391 Set output to "{output}{funcSrc}{nl}".
4392 Let mainSrc be decompileBlock(residual, 0).
4393 Set output to "{output}## Main{nl}{mainSrc}".
4394 Show output.
4395"#;
4396 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4397
4398 let result = run_logos_source(&combined)?;
4399 Ok(result)
4400}
4401
4402pub fn genuine_projection3_residual() -> Result<String, String> {
4413 let pe_bti = pe_bti_source_text();
4414 let pe = pe_source_text();
4415 let decompile = decompile_source_text();
4416
4417 let bti_types = CORE_TYPES_FOR_PE
4419 .replace("specResults", "memoCache")
4420 .replace("onStack", "callGuard");
4421
4422 let program = format!(
4424 "{}\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",
4425 bti_types, pe_bti
4426 );
4427
4428 let encoded = encode_program_source_compact(&program)
4430 .map_err(|e| format!("Failed to encode pe_bti: {:?}", e))?;
4431
4432 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4434 Let residual be peBlock(encodedMain, state).
4435 Let nl be chr(10).
4436 Let mutable output be "".
4437 Let specFuncs be peFuncs(state).
4438 Let specNames be collectCallNames(residual).
4439 Repeat for sn in specNames:
4440 Let snKey be "{sn}".
4441 If specFuncs contains snKey:
4442 Let fdef be item snKey of specFuncs.
4443 Let funcSrc be decompileFunc(fdef).
4444 If the length of funcSrc is greater than 0:
4445 Set output to "{output}{funcSrc}{nl}".
4446 Let mainSrc be decompileBlock(residual, 0).
4447 Set output to "{output}## Main{nl}{mainSrc}".
4448 Show output.
4449"#;
4450 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4451
4452 let result = run_logos_source(&combined)?;
4453 Ok(result)
4454}
4455
4456pub fn projection3_source_real(_core_types: &str) -> Result<String, String> {
4465 let pe_mini = pe_mini_source_text();
4466
4467 let cogen = replace_word(
4469 &replace_word(
4470 &replace_word(pe_mini, "peExprM", "cogenExpr"),
4471 "peBlockM", "cogenBlock",
4472 ),
4473 "extractReturnM", "extractReturn",
4474 );
4475
4476 Ok(cogen.to_string())
4477}
4478
4479fn replace_word(source: &str, from: &str, to: &str) -> String {
4480 let mut result = String::with_capacity(source.len());
4481 let mut remaining = source;
4482 while let Some(pos) = remaining.find(from) {
4483 let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
4484 let after_pos = pos + from.len();
4485 let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
4486 let is_word = !before.is_ascii_alphanumeric() && before != b'_'
4487 && !after.is_ascii_alphanumeric() && after != b'_';
4488 result.push_str(&remaining[..pos]);
4489 if is_word {
4490 result.push_str(to);
4491 } else {
4492 result.push_str(from);
4493 }
4494 remaining = &remaining[after_pos..];
4495 }
4496 result.push_str(remaining);
4497 result
4498}
4499
4500#[cfg(test)]
4501mod tests {
4502 use super::*;
4503
4504 #[test]
4505 fn test_compile_let_statement() {
4506 let source = "## Main\nLet x be 5.";
4507 let result = compile_to_rust(source);
4508 assert!(result.is_ok(), "Should compile: {:?}", result);
4509 let rust = result.unwrap();
4510 assert!(rust.contains("fn main()"));
4511 assert!(rust.contains("let x = 5;"));
4512 }
4513
4514 #[test]
4515 fn test_compile_return_statement() {
4516 let source = "## Main\nReturn 42.";
4517 let result = compile_to_rust(source);
4518 assert!(result.is_ok(), "Should compile: {:?}", result);
4519 let rust = result.unwrap();
4520 assert!(rust.contains("return 42;"));
4521 }
4522
4523}