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 vm_run_source(source: &str) -> Result<String, String> {
158 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
159 Ok((stmts, types, policies)) => {
160 let (output, error) =
161 crate::vm::run_to_outcome(stmts, interner, Some(types), Some(&policies));
162 match error {
163 None => Ok(output),
164 Some(e) => Err(e),
165 }
166 }
167 Err(advice) => Err(advice),
168 })
169}
170
171#[derive(Debug, Clone, PartialEq, Eq)]
175pub struct RunOutcome {
176 pub output: String,
177 pub error: Option<String>,
178}
179
180pub fn tw_outcome(source: &str) -> RunOutcome {
185 crate::concurrency::net_inbox::set_net_offline(true);
188 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
189 Ok((stmts, types, policies)) => {
190 let force_async = crate::interpreter::needs_async(stmts);
191 let result = crate::ui_bridge::run_treewalker(
192 stmts, types, policies, interner, force_async, &[],
193 );
194 RunOutcome { output: result.lines.join("\n"), error: result.error }
195 }
196 Err(advice) => RunOutcome { output: String::new(), error: Some(advice) },
197 })
198}
199
200pub fn vm_outcome(source: &str) -> RunOutcome {
203 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
204 Ok((stmts, types, policies)) => {
205 let (output, error) =
206 crate::vm::run_to_outcome(stmts, interner, Some(types), Some(&policies));
207 RunOutcome { output, error }
208 }
209 Err(advice) => RunOutcome { output: String::new(), error: Some(advice) },
210 })
211}
212
213#[cfg(not(target_arch = "wasm32"))]
219pub fn vm_outcome_concurrent(source: &str) -> RunOutcome {
220 let r = crate::ui_bridge::run_vm_concurrent(source);
221 RunOutcome { output: r.lines.join("\n"), error: r.error }
222}
223
224#[cfg(not(target_arch = "wasm32"))]
229pub fn vm_outcome_net(source: &str) -> RunOutcome {
230 crate::concurrency::net_inbox::set_net_offline(true);
232 let r = futures::executor::block_on(crate::ui_bridge::run_vm_net_async(source));
233 RunOutcome { output: r.lines.join("\n"), error: r.error }
234}
235
236pub fn tw_outcome_with_args(source: &str, program_args: &[String]) -> RunOutcome {
241 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
242 Ok((stmts, types, policies)) => {
243 let force_async = crate::interpreter::needs_async(stmts);
244 let result = crate::ui_bridge::run_treewalker(
245 stmts, types, policies, interner, force_async, program_args,
246 );
247 RunOutcome { output: result.lines.join("\n"), error: result.error }
248 }
249 Err(advice) => RunOutcome { output: String::new(), error: Some(advice) },
250 })
251}
252
253pub fn vm_outcome_with_args(
258 source: &str,
259 program_args: &[String],
260 tier: Option<&dyn crate::vm::NativeTier>,
261) -> RunOutcome {
262 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
263 Ok((stmts, types, policies)) => {
264 let (output, error) = crate::vm::run_to_outcome_with_args(
265 stmts,
266 interner,
267 Some(types),
268 Some(&policies),
269 program_args,
270 tier,
271 );
272 RunOutcome { output, error }
273 }
274 Err(advice) => RunOutcome { output: String::new(), error: Some(advice) },
275 })
276}
277
278pub fn vm_outcome_tiered(
283 source: &str,
284 program_args: &[String],
285 tier: crate::optimization::Tier,
286 native_tier: Option<&dyn crate::vm::NativeTier>,
287) -> RunOutcome {
288 crate::ui_bridge::with_optimized_program_tiered(source, tier, |parsed, interner| match parsed {
289 Ok((stmts, types, policies)) => {
290 let (output, error) = crate::vm::run_to_outcome_with_args(
291 stmts,
292 interner,
293 Some(types),
294 Some(&policies),
295 program_args,
296 native_tier,
297 );
298 RunOutcome { output, error }
299 }
300 Err(advice) => RunOutcome { output: String::new(), error: Some(advice) },
301 })
302}
303
304#[cfg(not(target_arch = "wasm32"))]
310pub fn vm_outcome_bg(source: &str, program_args: &[String]) -> RunOutcome {
311 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
312 Ok((stmts, types, policies)) => {
313 let (output, error) = crate::vm::run_to_outcome_bg(
314 stmts,
315 interner,
316 Some(types),
317 Some(&policies),
318 program_args,
319 );
320 RunOutcome { output, error }
321 }
322 Err(advice) => RunOutcome { output: String::new(), error: Some(advice) },
323 })
324}
325
326pub fn compile_to_rust(source: &str) -> Result<String, ParseError> {
359 compile_program_full(source).map(|o| o.rust_code)
360}
361
362pub fn compile_to_rust_deterministic(source: &str) -> Result<String, ParseError> {
368 crate::codegen::with_seeded_select(|| compile_to_rust(source))
369}
370
371pub fn compile_to_rust_with_proven(source: &str, proven: &str) -> Result<String, ParseError> {
376 compile_program_full_with_proven(source, Some(proven)).map(|o| o.rust_code)
377}
378
379pub fn optimizations_used(source: &str) -> Vec<&'static str> {
386 let base = match compile_to_rust(source) {
387 Ok(r) => r,
388 Err(_) => return Vec::new(),
389 };
390 crate::optimization::REGISTRY
391 .iter()
392 .filter(|m| {
393 let decorated = crate::optimization::decorate_source(source, &[m.keyword]);
394 compile_to_rust(&decorated).map(|r| r != base).unwrap_or(false)
395 })
396 .map(|m| m.keyword)
397 .collect()
398}
399
400pub fn compile_program_traced(
407 source: &str,
408) -> Result<(CompileOutput, crate::optimization::FiredOptimizations), ParseError> {
409 crate::optimize::begin_fired_trace();
410 let result = compile_program_full(source);
411 let fired = crate::optimize::end_fired_trace().unwrap_or_default();
414 result.map(|o| (o, fired))
415}
416
417pub fn optimizations_fired(source: &str) -> Vec<&'static str> {
420 compile_program_traced(source)
421 .map(|(_, fired)| fired.keywords())
422 .unwrap_or_default()
423}
424
425pub fn optimizations_fired_with(
429 source: &str,
430 cfg: &crate::optimization::OptimizationConfig,
431) -> Vec<&'static str> {
432 let disabled: Vec<&'static str> = cfg.disabled_keywords().collect();
433 let decorated = crate::optimization::decorate_source(source, &disabled);
434 optimizations_fired(&decorated)
435}
436
437pub fn compile_to_rust_traced(
441 source: &str,
442) -> Result<(String, Vec<&'static str>, Vec<(&'static str, &'static str)>), ParseError> {
443 crate::optimize::begin_fired_trace();
444 let result = compile_program_full(source);
445 let fired = crate::optimize::end_fired_trace().unwrap_or_default().keywords();
446 let preempted = preempted_keywords();
447 result.map(|o| (o.rust_code, fired, preempted))
448}
449
450pub fn optimization_preemptions(source: &str) -> Vec<(&'static str, &'static str)> {
453 crate::optimize::begin_fired_trace();
454 let _ = compile_program_full(source);
455 let _ = crate::optimize::end_fired_trace();
456 preempted_keywords()
457}
458
459fn preempted_keywords() -> Vec<(&'static str, &'static str)> {
461 crate::optimize::end_preempted_trace()
462 .into_iter()
463 .map(|(w, l)| (w.meta().keyword, l.meta().keyword))
464 .collect()
465}
466
467pub fn optimization_dependencies(source: &str) -> Vec<(&'static str, &'static str)> {
480 use crate::optimization::{by_keyword, decorate_source, OptimizationConfig};
481 use std::collections::BTreeSet;
482
483 let base: Vec<&'static str> = optimizations_fired(source);
484 let base_set: BTreeSet<&'static str> = base.iter().copied().collect();
485 let mut out: Vec<(&'static str, &'static str)> = Vec::new();
486 for &dep in &base {
487 let dep_opt = match by_keyword(dep) {
488 Some(o) => o,
489 None => continue,
490 };
491 let mut cfg = OptimizationConfig::all_on();
493 cfg.set(dep_opt, false);
494 cfg.normalize();
495 let off: BTreeSet<&'static str> =
496 optimizations_fired(&decorate_source(source, &[dep])).into_iter().collect();
497 for &dependent in &base_set {
498 if dependent == dep || off.contains(dependent) {
499 continue;
500 }
501 let dependent_opt = match by_keyword(dependent) {
502 Some(o) => o,
503 None => continue,
504 };
505 if cfg.is_on(dependent_opt) {
507 out.push((dependent, dep));
508 }
509 }
510 }
511 out.sort();
512 out.dedup();
513 out
514}
515
516pub fn optimization_graph(
523 source: &str,
524) -> (
525 Vec<&'static str>,
526 Vec<(&'static str, &'static str)>,
527 Vec<(&'static str, &'static str)>,
528) {
529 (
530 optimizations_fired(source),
531 optimization_preemptions(source),
532 optimization_dependencies(source),
533 )
534}
535
536pub fn optimizations_fired_run(source: &str) -> Vec<&'static str> {
542 crate::optimize::begin_fired_trace();
543 crate::ui_bridge::with_optimized_program(source, |_parsed, _interner| {});
544 crate::optimize::end_fired_trace()
545 .map(|fired| fired.keywords())
546 .unwrap_or_default()
547}
548
549pub fn optimizations_fired_vm(source: &str) -> Vec<&'static str> {
555 crate::optimize::begin_fired_trace();
556 crate::ui_bridge::with_optimized_program(source, |parsed, interner| {
557 if let Ok((stmts, types, _policies)) = parsed {
558 let oracle = crate::optimize::oracle_analyze_with(stmts, interner);
559 let _ = crate::vm::Compiler::compile_with_oracle(stmts, interner, Some(types), Some(oracle));
560 }
561 });
562 crate::optimize::end_fired_trace()
563 .map(|fired| fired.keywords())
564 .unwrap_or_default()
565}
566
567pub fn compile_to_c(source: &str) -> Result<String, ParseError> {
572 let mut interner = Interner::new();
573 let mut lexer = Lexer::new(source, &mut interner);
574 let tokens = lexer.tokenize();
575
576 let (type_registry, _policy_registry) = {
577 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
578 let result = discovery.run_full();
579 (result.types, result.policies)
580 };
581 let codegen_registry = type_registry.clone();
582
583 let mut world_state = WorldState::new();
584 let expr_arena = Arena::new();
585 let term_arena = Arena::new();
586 let np_arena = Arena::new();
587 let sym_arena = Arena::new();
588 let role_arena = Arena::new();
589 let pp_arena = Arena::new();
590 let stmt_arena: Arena<Stmt> = Arena::new();
591 let imperative_expr_arena: Arena<Expr> = Arena::new();
592 let type_expr_arena: Arena<TypeExpr> = Arena::new();
593
594 let ast_ctx = AstContext::with_types(
595 &expr_arena, &term_arena, &np_arena, &sym_arena,
596 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
597 &type_expr_arena,
598 );
599
600 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
601 let stmts = parser.parse_program()?;
602 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner, &crate::optimization::OptimizationConfig::from_env());
603
604 Ok(crate::codegen_c::codegen_program_c(&stmts, &codegen_registry, &interner))
605}
606
607pub fn compile_to_wasm(source: &str) -> Result<Vec<u8>, ParseError> {
617 fn custom(msg: String) -> ParseError {
618 ParseError { kind: crate::error::ParseErrorKind::Custom(msg), span: crate::token::Span::default() }
619 }
620 crate::ui_bridge::with_parsed_program(source, |parsed, interner| {
621 let (stmts, types, policies) = parsed.map_err(custom)?;
622 let oracle = crate::optimize::oracle_analyze_with(stmts, interner);
628 let program = crate::vm::Compiler::compile_with_oracle(stmts, interner, Some(types), Some(oracle))
629 .map_err(custom)?;
630 crate::vm::wasm::assemble_program(&program, &policies, interner).map_err(|e| custom(e.to_string()))
631 })
632}
633
634#[cfg(all(feature = "wasm-jit", not(target_arch = "wasm32")))]
646pub fn compile_to_wasm_linked(source: &str) -> Result<Vec<u8>, ParseError> {
647 fn custom(msg: String) -> ParseError {
648 ParseError { kind: crate::error::ParseErrorKind::Custom(msg), span: crate::token::Span::default() }
649 }
650 let relocatable = crate::ui_bridge::with_parsed_program(source, |parsed, interner| -> Result<Vec<u8>, ParseError> {
651 let (stmts, types, policies) = parsed.map_err(custom)?;
652 let oracle = crate::optimize::oracle_analyze_with(stmts, interner);
653 let program = crate::vm::Compiler::compile_with_oracle(stmts, interner, Some(types), Some(oracle)).map_err(custom)?;
654 let module = crate::vm::wasm::assemble_program_linked(&program, &policies, interner).map_err(|e| custom(e.to_string()))?;
655 crate::vm::wasm::module_to_relocatable(&module).map_err(|e| custom(e.to_string()))
656 })?;
657 crate::vm::wasm::link_relocatable_bigint(&relocatable).map_err(|e| custom(e.to_string()))
658}
659
660pub fn display_float_like_logos(f: f64) -> String {
665 crate::interpreter::RuntimeValue::Float(f).to_display_string()
666}
667
668pub fn classify_source(source: &str) -> Result<crate::concurrency::Determinacy, ParseError> {
675 let mut interner = Interner::new();
676 let mut lexer = Lexer::new(source, &mut interner);
677 let tokens = lexer.tokenize();
678
679 let (type_registry, _policy_registry) = {
680 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
681 let result = discovery.run_full();
682 (result.types, result.policies)
683 };
684
685 let mut world_state = WorldState::new();
686 let expr_arena = Arena::new();
687 let term_arena = Arena::new();
688 let np_arena = Arena::new();
689 let sym_arena = Arena::new();
690 let role_arena = Arena::new();
691 let pp_arena = Arena::new();
692 let stmt_arena: Arena<Stmt> = Arena::new();
693 let imperative_expr_arena: Arena<Expr> = Arena::new();
694 let type_expr_arena: Arena<TypeExpr> = Arena::new();
695
696 let ast_ctx = AstContext::with_types(
697 &expr_arena, &term_arena, &np_arena, &sym_arena,
698 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
699 &type_expr_arena,
700 );
701
702 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
703 let stmts = parser.parse_program()?;
704 Ok(crate::concurrency::classify_program(&stmts))
705}
706
707pub fn first_parallel_block_independent(source: &str) -> Result<Option<bool>, ParseError> {
711 let mut interner = Interner::new();
712 let mut lexer = Lexer::new(source, &mut interner);
713 let tokens = lexer.tokenize();
714
715 let (type_registry, _policy_registry) = {
716 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
717 let result = discovery.run_full();
718 (result.types, result.policies)
719 };
720
721 let mut world_state = WorldState::new();
722 let expr_arena = Arena::new();
723 let term_arena = Arena::new();
724 let np_arena = Arena::new();
725 let sym_arena = Arena::new();
726 let role_arena = Arena::new();
727 let pp_arena = Arena::new();
728 let stmt_arena: Arena<Stmt> = Arena::new();
729 let imperative_expr_arena: Arena<Expr> = Arena::new();
730 let type_expr_arena: Arena<TypeExpr> = Arena::new();
731
732 let ast_ctx = AstContext::with_types(
733 &expr_arena, &term_arena, &np_arena, &sym_arena,
734 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
735 &type_expr_arena,
736 );
737
738 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
739 let stmts = parser.parse_program()?;
740 Ok(find_first_parallel_independence(&stmts))
741}
742
743fn find_first_parallel_independence(stmts: &[Stmt]) -> Option<bool> {
744 for s in stmts {
745 match s {
746 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
747 return Some(crate::concurrency::branches_independent(tasks));
748 }
749 Stmt::FunctionDef { body, .. }
750 | Stmt::While { body, .. }
751 | Stmt::Repeat { body, .. }
752 | Stmt::Zone { body, .. } => {
753 if let Some(r) = find_first_parallel_independence(body) {
754 return Some(r);
755 }
756 }
757 Stmt::If { then_block, else_block, .. } => {
758 if let Some(r) = find_first_parallel_independence(then_block) {
759 return Some(r);
760 }
761 if let Some(eb) = else_block {
762 if let Some(r) = find_first_parallel_independence(eb) {
763 return Some(r);
764 }
765 }
766 }
767 _ => {}
768 }
769 }
770 None
771}
772
773pub fn send_check_source(source: &str) -> Result<Vec<crate::concurrency::SendDiagnostic>, ParseError> {
778 let mut interner = Interner::new();
779 let mut lexer = Lexer::new(source, &mut interner);
780 let tokens = lexer.tokenize();
781
782 let (type_registry, _policy_registry) = {
783 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
784 let result = discovery.run_full();
785 (result.types, result.policies)
786 };
787
788 let mut world_state = WorldState::new();
789 let expr_arena = Arena::new();
790 let term_arena = Arena::new();
791 let np_arena = Arena::new();
792 let sym_arena = Arena::new();
793 let role_arena = Arena::new();
794 let pp_arena = Arena::new();
795 let stmt_arena: Arena<Stmt> = Arena::new();
796 let imperative_expr_arena: Arena<Expr> = Arena::new();
797 let type_expr_arena: Arena<TypeExpr> = Arena::new();
798
799 let ast_ctx = AstContext::with_types(
800 &expr_arena, &term_arena, &np_arena, &sym_arena,
801 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
802 &type_expr_arena,
803 );
804
805 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
806 let stmts = parser.parse_program()?;
807 Ok(crate::concurrency::check_send_escape(&stmts))
808}
809
810pub fn compile_program_full_deterministic(source: &str) -> Result<CompileOutput, ParseError> {
818 crate::codegen::with_seeded_select(|| compile_program_full(source))
819}
820
821pub fn compile_program_full(source: &str) -> Result<CompileOutput, ParseError> {
822 let (imperative_src, math_src) = crate::ui_bridge::partition_mixed(source);
826 let proven = math_src.as_deref().and_then(crate::ui_bridge::mixed_proven_module);
827 compile_program_full_with_proven(&imperative_src, proven.as_deref())
828}
829
830pub fn compile_program_full_with_wire(source: &str) -> Result<CompileOutput, ParseError> {
836 crate::codegen::types::with_wire_impls(true, || compile_program_full(source))
837}
838
839pub fn compile_program_full_with_proven(source: &str, proven: Option<&str>) -> Result<CompileOutput, ParseError> {
845 let prelude_src = crate::loader::apply_prelude(source);
848 let source = prelude_src.as_ref();
849
850 let mut interner = Interner::new();
851 let mut lexer = Lexer::new(source, &mut interner);
852 let tokens = lexer.tokenize();
853
854 let (type_registry, policy_registry) = {
856 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
857 let result = discovery.run_full();
858 (result.types, result.policies)
859 };
860 let codegen_registry = type_registry.clone();
862 let codegen_policies = policy_registry.clone();
863
864 let mut world_state = WorldState::new();
865 let expr_arena = Arena::new();
866 let term_arena = Arena::new();
867 let np_arena = Arena::new();
868 let sym_arena = Arena::new();
869 let role_arena = Arena::new();
870 let pp_arena = Arena::new();
871 let stmt_arena: Arena<Stmt> = Arena::new();
872 let imperative_expr_arena: Arena<Expr> = Arena::new();
873 let type_expr_arena: Arena<TypeExpr> = Arena::new();
874
875 let ast_ctx = AstContext::with_types(
876 &expr_arena,
877 &term_arena,
878 &np_arena,
879 &sym_arena,
880 &role_arena,
881 &pp_arena,
882 &stmt_arena,
883 &imperative_expr_arena,
884 &type_expr_arena,
885 );
886
887 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
889 let stmts = parser.parse_program()?;
892 let opt_config = {
897 let mut c = crate::optimization::OptimizationConfig::from_env()
898 .merged(&parser.program_opt_flags());
899 c.normalize();
900 c
901 };
902
903 let resolved = crate::resolve_division::resolve_divisions(
908 &stmts,
909 &stmt_arena,
910 &imperative_expr_arena,
911 &interner,
912 opt_config.is_on(crate::optimization::Opt::Comptime),
913 );
914 let stmts: Vec<Stmt> = match resolved {
915 Some(rw) => rw.to_vec(),
916 None => stmts,
917 };
918
919 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner, &opt_config);
921
922 let mut dependencies = extract_dependencies(&stmts, &interner)?;
924
925 let needs_wasm_bindgen = stmts.iter().any(|stmt| {
927 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
928 interner.resolve(*target).eq_ignore_ascii_case("wasm")
929 } else {
930 false
931 }
932 });
933 if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
934 dependencies.push(CrateDependency {
935 name: "wasm-bindgen".to_string(),
936 version: "0.2".to_string(),
937 features: vec![],
938 });
939 }
940
941 let mut escape_checker = EscapeChecker::new(&interner);
944 escape_checker.check_program(&stmts).map_err(|e| {
945 ParseError {
948 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
949 span: e.span,
950 }
951 })?;
952
953 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
957 .map_err(|e| ParseError {
958 kind: e.to_parse_error_kind(&interner),
959 span: crate::token::Span::default(),
960 })?;
961 let rust_code = crate::codegen::codegen_program_with_proven(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env, &opt_config, "proven", proven);
962
963 let has_c = stmts.iter().any(|stmt| {
965 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
966 match export_target {
967 None => true,
968 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
969 }
970 } else {
971 false
972 }
973 });
974
975 let c_header = if has_c {
976 Some(generate_c_header(&stmts, "module", &interner, &codegen_registry))
977 } else {
978 None
979 };
980
981 if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
983 dependencies.push(CrateDependency {
984 name: "serde_json".to_string(),
985 version: "1".to_string(),
986 features: vec![],
987 });
988 }
989
990 let python_bindings = if has_c {
991 Some(generate_python_bindings(&stmts, "module", &interner, &codegen_registry))
992 } else {
993 None
994 };
995
996 let (typescript_bindings, typescript_types) = if has_c {
997 let (js, dts) = generate_typescript_bindings(&stmts, "module", &interner, &codegen_registry);
998 (Some(js), Some(dts))
999 } else {
1000 (None, None)
1001 };
1002
1003 Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
1004}
1005
1006pub fn compile_function_to_native_rust(
1017 source: &str,
1018 target: &str,
1019) -> Result<Option<AotModule>, ParseError> {
1020 let mut interner = Interner::new();
1021 let mut lexer = Lexer::new(source, &mut interner);
1022 let tokens = lexer.tokenize();
1023
1024 let (type_registry, policy_registry) = {
1025 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1026 let result = discovery.run_full();
1027 (result.types, result.policies)
1028 };
1029 let codegen_registry = type_registry.clone();
1030 let codegen_policies = policy_registry.clone();
1031
1032 let mut world_state = WorldState::new();
1033 let expr_arena = Arena::new();
1034 let term_arena = Arena::new();
1035 let np_arena = Arena::new();
1036 let sym_arena = Arena::new();
1037 let role_arena = Arena::new();
1038 let pp_arena = Arena::new();
1039 let stmt_arena: Arena<Stmt> = Arena::new();
1040 let imperative_expr_arena: Arena<Expr> = Arena::new();
1041 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1042 let ast_ctx = AstContext::with_types(
1043 &expr_arena,
1044 &term_arena,
1045 &np_arena,
1046 &sym_arena,
1047 &role_arena,
1048 &pp_arena,
1049 &stmt_arena,
1050 &imperative_expr_arena,
1051 &type_expr_arena,
1052 );
1053
1054 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
1055 let stmts = parser.parse_program()?;
1056 let opt_config = {
1057 let mut c = crate::optimization::OptimizationConfig::from_env()
1058 .merged(&parser.program_opt_flags());
1059 c.normalize();
1060 c
1061 };
1062 let stmts = crate::optimize::optimize_program(
1065 stmts,
1066 &imperative_expr_arena,
1067 &stmt_arena,
1068 &mut interner,
1069 &opt_config,
1070 );
1071
1072 let target_sig = stmts.iter().find_map(|s| match s {
1074 Stmt::FunctionDef { name, params, return_type, .. }
1075 if interner.resolve(*name) == target =>
1076 {
1077 let ps: Vec<(crate::intern::Symbol, &TypeExpr)> =
1078 params.iter().map(|(s, t)| (*s, *t)).collect();
1079 Some((*name, ps, *return_type))
1080 }
1081 _ => None,
1082 });
1083 let Some((target_sym, params, return_type)) = target_sig else {
1084 return Ok(None);
1085 };
1086 let Some(shim) =
1088 crate::codegen::codegen_native_tier_export(target_sym, ¶ms, return_type, &interner)
1089 else {
1090 return Ok(None);
1091 };
1092
1093 let slice = crate::codegen::function_slice(&stmts, target_sym, &interner);
1094 let type_env = crate::analysis::check_program(&slice, &interner, &codegen_registry).map_err(|e| {
1095 ParseError {
1096 kind: e.to_parse_error_kind(&interner),
1097 span: crate::token::Span::default(),
1098 }
1099 })?;
1100 let module = crate::codegen::codegen_program(
1101 &slice,
1102 &codegen_registry,
1103 &codegen_policies,
1104 &interner,
1105 &type_env,
1106 &opt_config,
1107 );
1108 let symbol = format!(
1109 "logos_native_{}",
1110 crate::analysis::types::RustNames::new(&interner).raw(target_sym)
1111 );
1112 Ok(Some(AotModule {
1113 rust: format!("{module}\n{shim}"),
1114 symbol,
1115 arity: params.len(),
1116 }))
1117}
1118
1119#[derive(Debug, Clone)]
1122pub struct AotModule {
1123 pub rust: String,
1124 pub symbol: String,
1125 pub arity: usize,
1126}
1127
1128#[cfg(not(target_arch = "wasm32"))]
1136pub fn build_native_wasm(
1137 rust_source: &str,
1138 crate_name: &str,
1139 work_dir: &std::path::Path,
1140) -> Result<std::path::PathBuf, String> {
1141 use std::process::Command;
1142 let proj = work_dir.join(crate_name);
1143 let _ = fs::remove_dir_all(&proj);
1144 fs::create_dir_all(proj.join("src")).map_err(|e| e.to_string())?;
1145 fs::write(proj.join("src").join("lib.rs"), rust_source).map_err(|e| e.to_string())?;
1146
1147 let cargo_toml = format!(
1148 "[package]\nname = \"{crate_name}\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n\
1149 [lib]\ncrate-type = [\"cdylib\"]\n\n\
1150 [dependencies]\n\
1151 logicaffeine-data = {{ path = \"./crates/logicaffeine_data\" }}\n\
1152 logicaffeine-system = {{ path = \"./crates/logicaffeine_system\" }}\n\n\
1153 [profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\nstrip = true\n"
1154 );
1155 fs::write(proj.join("Cargo.toml"), &cargo_toml).map_err(|e| e.to_string())?;
1156
1157 copy_runtime_crates(&proj).map_err(|e| e.to_string())?;
1158
1159 let out = Command::new("cargo")
1160 .args(["build", "--release", "--lib", "--target", "wasm32-unknown-unknown"])
1161 .current_dir(&proj)
1162 .output()
1163 .map_err(|e| format!("failed to run cargo: {e}"))?;
1164 if !out.status.success() {
1165 return Err(format!(
1166 "wasm bundle build failed:\n{}",
1167 String::from_utf8_lossy(&out.stderr)
1168 ));
1169 }
1170
1171 let wasm = proj
1172 .join("target")
1173 .join("wasm32-unknown-unknown")
1174 .join("release")
1175 .join(format!("{crate_name}.wasm"));
1176 if !wasm.exists() {
1177 return Err(format!("wasm module not found at {}", wasm.display()));
1178 }
1179 Ok(wasm)
1180}
1181
1182#[cfg(not(target_arch = "wasm32"))]
1190pub fn build_native_cdylib(
1191 rust_source: &str,
1192 crate_name: &str,
1193 work_dir: &std::path::Path,
1194) -> Result<std::path::PathBuf, String> {
1195 use std::process::Command;
1196 let proj = work_dir.join(crate_name);
1197 let _ = fs::remove_dir_all(&proj);
1198 fs::create_dir_all(proj.join("src")).map_err(|e| e.to_string())?;
1199 fs::write(proj.join("src").join("lib.rs"), rust_source).map_err(|e| e.to_string())?;
1200
1201 let cargo_toml = format!(
1202 "[package]\nname = \"{crate_name}\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n\
1203 [lib]\ncrate-type = [\"cdylib\"]\n\n\
1204 [dependencies]\n\
1205 logicaffeine-data = {{ path = \"./crates/logicaffeine_data\" }}\n\
1206 logicaffeine-system = {{ path = \"./crates/logicaffeine_system\", features = [\"full\"] }}\n\
1207 tokio = {{ version = \"1\", features = [\"rt-multi-thread\", \"macros\"] }}\n\n\
1208 [profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\nstrip = true\n"
1209 );
1210 fs::write(proj.join("Cargo.toml"), &cargo_toml).map_err(|e| e.to_string())?;
1211
1212 let cargo_cfg = proj.join(".cargo");
1213 fs::create_dir_all(&cargo_cfg).map_err(|e| e.to_string())?;
1214 fs::write(
1215 cargo_cfg.join("config.toml"),
1216 "[build]\nrustflags = [\"-C\", \"target-cpu=native\"]\n",
1217 )
1218 .map_err(|e| e.to_string())?;
1219
1220 copy_runtime_crates(&proj).map_err(|e| e.to_string())?;
1221
1222 let out = Command::new("cargo")
1223 .args(["build", "--release", "--lib"])
1224 .current_dir(&proj)
1225 .output()
1226 .map_err(|e| format!("failed to run cargo: {e}"))?;
1227 if !out.status.success() {
1228 return Err(format!(
1229 "AOT cdylib build failed:\n{}",
1230 String::from_utf8_lossy(&out.stderr)
1231 ));
1232 }
1233
1234 let so = proj.join("target").join("release").join(format!(
1235 "{}{}{}",
1236 std::env::consts::DLL_PREFIX,
1237 crate_name,
1238 std::env::consts::DLL_SUFFIX
1239 ));
1240 if !so.exists() {
1241 return Err(format!("AOT cdylib not found at {}", so.display()));
1242 }
1243 Ok(so)
1244}
1245
1246#[cfg(not(target_arch = "wasm32"))]
1253pub fn aot_cache_key(rust_source: &str) -> String {
1254 let toolchain = std::process::Command::new("rustc")
1255 .arg("--version")
1256 .output()
1257 .ok()
1258 .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
1259 .unwrap_or_default();
1260 let mut h: u64 = 0xcbf29ce4_84222325;
1261 for b in toolchain.bytes().chain(rust_source.bytes()) {
1262 h ^= b as u64;
1263 h = h.wrapping_mul(0x0000_0100_0000_01b3);
1264 }
1265 format!("{h:016x}")
1266}
1267
1268#[cfg(not(target_arch = "wasm32"))]
1273pub fn build_native_cdylib_cached(
1274 rust_source: &str,
1275 fn_name: &str,
1276 cache_dir: &std::path::Path,
1277) -> Result<std::path::PathBuf, String> {
1278 let key = aot_cache_key(rust_source);
1279 let crate_name = format!("aot_{fn_name}_{key}");
1280 let so = cache_dir.join(&crate_name).join("target").join("release").join(format!(
1281 "{}{}{}",
1282 std::env::consts::DLL_PREFIX,
1283 crate_name,
1284 std::env::consts::DLL_SUFFIX
1285 ));
1286 if so.exists() {
1287 return Ok(so); }
1289 fs::create_dir_all(cache_dir).map_err(|e| e.to_string())?;
1290 build_native_cdylib(rust_source, &crate_name, cache_dir)
1291}
1292
1293#[cfg(not(target_arch = "wasm32"))]
1300pub fn aot_build_function(
1301 source: &str,
1302 fn_name: &str,
1303 cache_dir: &std::path::Path,
1304) -> Option<Box<dyn crate::vm::NativeFn>> {
1305 let module = compile_function_to_native_rust(source, fn_name).ok().flatten()?;
1306 let so = build_native_cdylib_cached(&module.rust, fn_name, cache_dir).ok()?;
1307 let (nf, _calls) = crate::vm::aot_tier::load_aot_native(&so, &module.symbol, module.arity)?;
1308 Some(nf)
1309}
1310
1311pub fn native_export_function_names(source: &str) -> Vec<String> {
1315 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
1316 Ok((stmts, _types, _policies)) => stmts
1317 .iter()
1318 .filter_map(|s| match s {
1319 Stmt::FunctionDef { name, is_exported: true, export_target: Some(t), .. }
1320 if interner.resolve(*t).eq_ignore_ascii_case("native") =>
1321 {
1322 Some(interner.resolve(*name).to_string())
1323 }
1324 _ => None,
1325 })
1326 .collect(),
1327 Err(_) => Vec::new(),
1328 })
1329}
1330
1331#[cfg(not(target_arch = "wasm32"))]
1338pub fn aot_load_bundle(
1339 source: &str,
1340 cache_dir: &std::path::Path,
1341) -> Vec<(String, Box<dyn crate::vm::NativeFn>)> {
1342 native_export_function_names(source)
1343 .into_iter()
1344 .filter_map(|name| aot_build_function(source, &name, cache_dir).map(|nf| (name, nf)))
1345 .collect()
1346}
1347
1348#[cfg(not(target_arch = "wasm32"))]
1354pub fn build_native_bundle(
1355 source: &str,
1356 bundle_dir: &std::path::Path,
1357) -> Result<Vec<(String, std::path::PathBuf)>, ParseError> {
1358 let mut manifest = Vec::new();
1359 for name in native_export_function_names(source) {
1360 let Some(module) = compile_function_to_native_rust(source, &name)? else {
1361 continue; };
1363 if let Ok(so) = build_native_cdylib_cached(&module.rust, &name, bundle_dir) {
1364 manifest.push((name, so));
1365 }
1366 }
1367 Ok(manifest)
1368}
1369
1370fn extract_dependencies(stmts: &[Stmt], interner: &Interner) -> Result<Vec<CrateDependency>, ParseError> {
1376 use std::collections::HashMap;
1377
1378 let mut seen: HashMap<String, String> = HashMap::new(); let mut deps: Vec<CrateDependency> = Vec::new();
1380
1381 for stmt in stmts {
1382 if let Stmt::Require { crate_name, version, features, span } = stmt {
1383 let name = interner.resolve(*crate_name).to_string();
1384 let ver = interner.resolve(*version).to_string();
1385
1386 if let Some(existing_ver) = seen.get(&name) {
1387 if *existing_ver != ver {
1388 return Err(ParseError {
1389 kind: crate::error::ParseErrorKind::Custom(format!(
1390 "Conflicting versions for crate \"{}\": \"{}\" and \"{}\".",
1391 name, existing_ver, ver
1392 )),
1393 span: *span,
1394 });
1395 }
1396 } else {
1398 seen.insert(name.clone(), ver.clone());
1399 deps.push(CrateDependency {
1400 name,
1401 version: ver,
1402 features: features.iter().map(|f| interner.resolve(*f).to_string()).collect(),
1403 });
1404 }
1405 }
1406 }
1407
1408 Ok(deps)
1409}
1410
1411pub fn compile_to_rust_checked(source: &str) -> Result<String, ParseError> {
1447 let (imperative_src, _) = crate::ui_bridge::partition_mixed(source);
1450 let source = imperative_src.as_str();
1451 let mut interner = Interner::new();
1452 let mut lexer = Lexer::new(source, &mut interner);
1453 let tokens = lexer.tokenize();
1454
1455 let (type_registry, policy_registry) = {
1457 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1458 let result = discovery.run_full();
1459 (result.types, result.policies)
1460 };
1461 let codegen_registry = type_registry.clone();
1463 let codegen_policies = policy_registry.clone();
1464
1465 let mut world_state = WorldState::new();
1466 let expr_arena = Arena::new();
1467 let term_arena = Arena::new();
1468 let np_arena = Arena::new();
1469 let sym_arena = Arena::new();
1470 let role_arena = Arena::new();
1471 let pp_arena = Arena::new();
1472 let stmt_arena: Arena<Stmt> = Arena::new();
1473 let imperative_expr_arena: Arena<Expr> = Arena::new();
1474 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1475
1476 let ast_ctx = AstContext::with_types(
1477 &expr_arena,
1478 &term_arena,
1479 &np_arena,
1480 &sym_arena,
1481 &role_arena,
1482 &pp_arena,
1483 &stmt_arena,
1484 &imperative_expr_arena,
1485 &type_expr_arena,
1486 );
1487
1488 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
1490 let stmts = parser.parse_program()?;
1491
1492 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner, &crate::optimization::OptimizationConfig::from_env());
1494
1495 let mut escape_checker = EscapeChecker::new(&interner);
1497 escape_checker.check_program(&stmts).map_err(|e| {
1498 ParseError {
1499 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
1500 span: e.span,
1501 }
1502 })?;
1503
1504 let mut ownership_checker = OwnershipChecker::new(&interner);
1507 ownership_checker.check_program(&stmts).map_err(|e| {
1508 ParseError {
1509 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
1510 span: e.span,
1511 }
1512 })?;
1513
1514 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
1515 .map_err(|e| ParseError {
1516 kind: e.to_parse_error_kind(&interner),
1517 span: crate::token::Span::default(),
1518 })?;
1519 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env, &crate::optimization::OptimizationConfig::from_env());
1520
1521 Ok(rust_code)
1522}
1523
1524#[cfg(feature = "verification")]
1571pub fn compile_to_rust_verified(source: &str) -> Result<String, ParseError> {
1572 use crate::verification::VerificationPass;
1573
1574 let mut interner = Interner::new();
1575 let mut lexer = Lexer::new(source, &mut interner);
1576 let tokens = lexer.tokenize();
1577
1578 let (type_registry, policy_registry) = {
1580 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1581 let result = discovery.run_full();
1582 (result.types, result.policies)
1583 };
1584 let codegen_registry = type_registry.clone();
1586 let codegen_policies = policy_registry.clone();
1587
1588 let mut world_state = WorldState::new();
1589 let expr_arena = Arena::new();
1590 let term_arena = Arena::new();
1591 let np_arena = Arena::new();
1592 let sym_arena = Arena::new();
1593 let role_arena = Arena::new();
1594 let pp_arena = Arena::new();
1595 let stmt_arena: Arena<Stmt> = Arena::new();
1596 let imperative_expr_arena: Arena<Expr> = Arena::new();
1597 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1598
1599 let ast_ctx = AstContext::with_types(
1600 &expr_arena,
1601 &term_arena,
1602 &np_arena,
1603 &sym_arena,
1604 &role_arena,
1605 &pp_arena,
1606 &stmt_arena,
1607 &imperative_expr_arena,
1608 &type_expr_arena,
1609 );
1610
1611 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
1613 let stmts = parser.parse_program()?;
1614
1615 let mut escape_checker = EscapeChecker::new(&interner);
1617 escape_checker.check_program(&stmts).map_err(|e| {
1618 ParseError {
1619 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
1620 span: e.span,
1621 }
1622 })?;
1623
1624 let mut verifier = VerificationPass::new(&interner);
1626 verifier.verify_program(&stmts).map_err(|e| {
1627 ParseError {
1628 kind: crate::error::ParseErrorKind::Custom(format!(
1629 "Verification Failed:\n\n{}",
1630 e
1631 )),
1632 span: crate::token::Span::default(),
1633 }
1634 })?;
1635
1636 let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
1637 .map_err(|e| ParseError {
1638 kind: e.to_parse_error_kind(&interner),
1639 span: crate::token::Span::default(),
1640 })?;
1641 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env, &crate::optimization::OptimizationConfig::from_env());
1642
1643 Ok(rust_code)
1644}
1645
1646pub struct CheckArtifacts {
1681 pub rust_code: String,
1682 pub dependencies: Vec<CrateDependency>,
1683 pub source_map: crate::sourcemap::SourceMap,
1684 pub interner: Interner,
1685}
1686
1687pub fn rustc_check_artifacts(source: &str) -> Result<CheckArtifacts, ParseError> {
1697 let user_source = source;
1698
1699 let prelude_src = crate::loader::apply_prelude(user_source);
1702 let expanded: &str = prelude_src.as_ref();
1703 debug_assert!(
1704 expanded.ends_with(user_source),
1705 "apply_prelude must PREPEND; span translation depends on it"
1706 );
1707 let prelude_offset = expanded.len() - user_source.len();
1708
1709 let mut interner = Interner::new();
1710 let mut lexer = Lexer::new(expanded, &mut interner);
1711 let tokens = lexer.tokenize();
1712
1713 let (type_registry, policy_registry) = {
1714 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1715 let result = discovery.run_full();
1716 (result.types, result.policies)
1717 };
1718 let codegen_registry = type_registry.clone();
1719 let codegen_policies = policy_registry.clone();
1720
1721 let mut world_state = WorldState::new();
1722 let expr_arena = Arena::new();
1723 let term_arena = Arena::new();
1724 let np_arena = Arena::new();
1725 let sym_arena = Arena::new();
1726 let role_arena = Arena::new();
1727 let pp_arena = Arena::new();
1728 let stmt_arena: Arena<Stmt> = Arena::new();
1729 let imperative_expr_arena: Arena<Expr> = Arena::new();
1730 let type_expr_arena: Arena<TypeExpr> = Arena::new();
1731
1732 let ast_ctx = AstContext::with_types(
1733 &expr_arena,
1734 &term_arena,
1735 &np_arena,
1736 &sym_arena,
1737 &role_arena,
1738 &pp_arena,
1739 &stmt_arena,
1740 &imperative_expr_arena,
1741 &type_expr_arena,
1742 );
1743
1744 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
1745 let stmts = parser.parse_program()?;
1746
1747 let stmt_spans: Vec<crate::token::Span> = parser
1750 .stmt_spans()
1751 .iter()
1752 .map(|span| {
1753 if span.start >= prelude_offset {
1754 crate::token::Span::new(span.start - prelude_offset, span.end - prelude_offset)
1755 } else {
1756 crate::token::Span::default()
1757 }
1758 })
1759 .collect();
1760
1761 let resolved = crate::resolve_division::resolve_divisions(
1764 &stmts,
1765 &stmt_arena,
1766 &imperative_expr_arena,
1767 &interner,
1768 false,
1769 );
1770 let stmts: Vec<Stmt> = match resolved {
1771 Some(rw) => rw.to_vec(),
1772 None => stmts,
1773 };
1774
1775 let (type_env, _findings) =
1777 crate::analysis::check_program_collect(&stmts, &interner, &codegen_registry);
1778
1779 let dependencies = extract_dependencies(&stmts, &interner)?;
1780
1781 let (rust_code, source_map) = crate::codegen::codegen_program_mapped(
1782 &stmts,
1783 &codegen_registry,
1784 &codegen_policies,
1785 &interner,
1786 &type_env,
1787 &crate::optimization::OptimizationConfig::all_off(),
1788 &stmt_spans,
1789 user_source,
1790 );
1791
1792 Ok(CheckArtifacts {
1793 rust_code,
1794 dependencies,
1795 source_map,
1796 interner,
1797 })
1798}
1799
1800pub fn rustc_check(source: &str, cache_dir: &Path) -> Result<Vec<LogosError>, CompileError> {
1811 let artifacts = rustc_check_artifacts(source).map_err(CompileError::Parse)?;
1812 write_cargo_project(&artifacts.rust_code, &artifacts.dependencies, cache_dir)?;
1813
1814 let check_output = Command::new("cargo")
1815 .arg("check")
1816 .arg("--message-format=json")
1817 .current_dir(cache_dir)
1818 .output()
1819 .map_err(|e| CompileError::Io(e.to_string()))?;
1820
1821 let stdout = String::from_utf8_lossy(&check_output.stdout);
1822 let diagnostics = parse_rustc_json(&stdout);
1823 let findings =
1824 crate::diagnostic::translate_diagnostics_all(&diagnostics, &artifacts.source_map, &artifacts.interner);
1825
1826 if !check_output.status.success() && findings.is_empty() {
1827 let stderr = String::from_utf8_lossy(&check_output.stderr);
1828 return Err(CompileError::Build(stderr.to_string()));
1829 }
1830
1831 Ok(findings)
1832}
1833
1834pub fn compile_to_dir(source: &str, output_dir: &Path) -> Result<(), CompileError> {
1835 let output = compile_program_full(source).map_err(CompileError::Parse)?;
1836 write_cargo_project(&output.rust_code, &output.dependencies, output_dir)
1837}
1838
1839pub fn write_cargo_project(
1844 rust_code: &str,
1845 dependencies: &[CrateDependency],
1846 output_dir: &Path,
1847) -> Result<(), CompileError> {
1848 let src_dir = output_dir.join("src");
1850 fs::create_dir_all(&src_dir).map_err(|e| CompileError::Io(e.to_string()))?;
1851
1852 let main_path = src_dir.join("main.rs");
1854 let mut file = fs::File::create(&main_path).map_err(|e| CompileError::Io(e.to_string()))?;
1855 file.write_all(rust_code.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
1856
1857 let mut cargo_toml = String::from(r#"[package]
1859name = "logos_output"
1860version = "0.1.0"
1861edition = "2021"
1862
1863[dependencies]
1864logicaffeine-data = { path = "./crates/logicaffeine_data" }
1865logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full"] }
1866tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
1867"#);
1868
1869 for dep in dependencies {
1873 if dep.features.is_empty() {
1874 let _ = writeln!(cargo_toml, "{} = \"{}\"", dep.name, dep.version);
1875 } else {
1876 let feats = dep.features.iter()
1877 .map(|f| format!("\"{}\"", f))
1878 .collect::<Vec<_>>()
1879 .join(", ");
1880 let _ = writeln!(
1881 cargo_toml,
1882 "{} = {{ version = \"{}\", features = [{}] }}",
1883 dep.name, dep.version, feats
1884 );
1885 }
1886 }
1887
1888 cargo_toml.push_str(r#"
1889[target.'cfg(target_os = "linux")'.dependencies]
1890logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full", "io-uring"] }
1891"#);
1892
1893 cargo_toml.push_str("\n[profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\npanic = \"abort\"\nstrip = true\n");
1894
1895 let cargo_path = output_dir.join("Cargo.toml");
1896 let mut file = fs::File::create(&cargo_path).map_err(|e| CompileError::Io(e.to_string()))?;
1897 file.write_all(cargo_toml.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
1898
1899 let cargo_config_dir = output_dir.join(".cargo");
1902 fs::create_dir_all(&cargo_config_dir).map_err(|e| CompileError::Io(e.to_string()))?;
1903 let config_content = "[build]\nrustflags = [\"-C\", \"target-cpu=native\"]\n";
1904 let config_path = cargo_config_dir.join("config.toml");
1905 fs::write(&config_path, config_content).map_err(|e| CompileError::Io(e.to_string()))?;
1906
1907 copy_runtime_crates(output_dir)?;
1909
1910 Ok(())
1911}
1912
1913pub fn copy_runtime_crates(output_dir: &Path) -> Result<(), CompileError> {
1916 let crates_dir = output_dir.join("crates");
1917 fs::create_dir_all(&crates_dir).map_err(|e| CompileError::Io(e.to_string()))?;
1918
1919 let workspace_root = find_workspace_root()?;
1921
1922 let data_src = workspace_root.join(CRATES_DATA_PATH);
1924 let data_dest = crates_dir.join("logicaffeine_data");
1925 copy_dir_recursive(&data_src, &data_dest)?;
1926 deworkspace_cargo_toml(&data_dest.join("Cargo.toml"))?;
1927
1928 let system_src = workspace_root.join(CRATES_SYSTEM_PATH);
1930 let system_dest = crates_dir.join("logicaffeine_system");
1931 copy_dir_recursive(&system_src, &system_dest)?;
1932 deworkspace_cargo_toml(&system_dest.join("Cargo.toml"))?;
1933
1934 let base_src = workspace_root.join("crates/logicaffeine_base");
1936 let base_dest = crates_dir.join("logicaffeine_base");
1937 copy_dir_recursive(&base_src, &base_dest)?;
1938 deworkspace_cargo_toml(&base_dest.join("Cargo.toml"))?;
1939
1940 Ok(())
1941}
1942
1943fn deworkspace_cargo_toml(cargo_toml_path: &Path) -> Result<(), CompileError> {
1949 let content = fs::read_to_string(cargo_toml_path)
1950 .map_err(|e| CompileError::Io(e.to_string()))?;
1951
1952 let mut result = String::with_capacity(content.len());
1953 for line in content.lines() {
1954 let trimmed = line.trim();
1955 if trimmed == "edition.workspace = true" {
1956 result.push_str("edition = \"2021\"");
1957 } else if trimmed == "rust-version.workspace = true" {
1958 result.push_str("rust-version = \"1.75\"");
1959 } else if trimmed == "authors.workspace = true"
1960 || trimmed == "repository.workspace = true"
1961 || trimmed == "homepage.workspace = true"
1962 || trimmed == "documentation.workspace = true"
1963 || trimmed == "keywords.workspace = true"
1964 || trimmed == "categories.workspace = true"
1965 || trimmed == "license.workspace = true"
1966 {
1967 continue;
1969 } else if trimmed.contains(".workspace = true") {
1970 continue;
1972 } else if let Some(rewritten) = deworkspace_dep_line(trimmed)? {
1973 result.push_str(&rewritten);
1977 } else {
1978 result.push_str(line);
1979 }
1980 result.push('\n');
1981 }
1982
1983 fs::write(cargo_toml_path, result)
1984 .map_err(|e| CompileError::Io(e.to_string()))?;
1985
1986 Ok(())
1987}
1988
1989fn deworkspace_dep_line(trimmed: &str) -> Result<Option<String>, CompileError> {
1997 let Some((name_part, rest)) = trimmed.split_once('=') else {
1998 return Ok(None);
1999 };
2000 let rest = rest.trim();
2001 if !rest.starts_with('{') || !rest.ends_with('}') || !rest.contains("workspace = true") {
2002 return Ok(None);
2003 }
2004 let name = name_part.trim();
2005 if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') {
2006 return Ok(None);
2007 }
2008 let extra_keys: Vec<&str> = rest[1..rest.len() - 1]
2009 .split(',')
2010 .map(str::trim)
2011 .filter(|k| !k.is_empty() && *k != "workspace = true")
2012 .collect();
2013 if !name.starts_with("logicaffeine-") {
2014 return Err(CompileError::Io(format!(
2015 "staged crate inherits third-party dependency `{}` from the workspace — \
2016 declare a concrete version in the crate's Cargo.toml (the staged copy \
2017 has no workspace root to inherit from)",
2018 name
2019 )));
2020 }
2021 let dir = name.replace('-', "_");
2022 let mut entry = format!("{} = {{ path = \"../{}\"", name, dir);
2023 for key in extra_keys {
2024 entry.push_str(", ");
2025 entry.push_str(key);
2026 }
2027 entry.push_str(" }");
2028 Ok(Some(entry))
2029}
2030
2031fn find_workspace_root() -> Result<std::path::PathBuf, CompileError> {
2033 if let Ok(workspace) = std::env::var("LOGOS_WORKSPACE") {
2035 let path = Path::new(&workspace);
2036 if path.join("Cargo.toml").exists() && path.join("crates").exists() {
2037 return Ok(path.to_path_buf());
2038 }
2039 }
2040
2041 if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
2043 let path = Path::new(&manifest_dir);
2044 if let Some(parent) = path.parent().and_then(|p| p.parent()) {
2045 if parent.join("Cargo.toml").exists() {
2046 return Ok(parent.to_path_buf());
2047 }
2048 }
2049 }
2050
2051 if let Ok(exe) = std::env::current_exe() {
2054 if let Some(dir) = exe.parent() {
2055 let mut candidate = dir.to_path_buf();
2057 for _ in 0..5 {
2058 if candidate.join("Cargo.toml").exists() && candidate.join("crates").exists() {
2059 return Ok(candidate);
2060 }
2061 if !candidate.pop() {
2062 break;
2063 }
2064 }
2065 }
2066 }
2067
2068 let mut current = std::env::current_dir()
2070 .map_err(|e| CompileError::Io(e.to_string()))?;
2071
2072 loop {
2073 if current.join("Cargo.toml").exists() && current.join("crates").exists() {
2074 return Ok(current);
2075 }
2076 if !current.pop() {
2077 return Err(CompileError::Io(
2078 "Could not find workspace root. Set LOGOS_WORKSPACE env var or run from within the workspace.".to_string()
2079 ));
2080 }
2081 }
2082}
2083
2084fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), CompileError> {
2087 fs::create_dir_all(dst).map_err(|e| CompileError::Io(e.to_string()))?;
2088
2089 for entry in fs::read_dir(src).map_err(|e| CompileError::Io(e.to_string()))? {
2090 let entry = entry.map_err(|e| CompileError::Io(e.to_string()))?;
2091 let src_path = entry.path();
2092 let file_name = entry.file_name();
2093 let dst_path = dst.join(&file_name);
2094
2095 if file_name == "target"
2097 || file_name == ".git"
2098 || file_name == "Cargo.lock"
2099 || file_name == ".DS_Store"
2100 {
2101 continue;
2102 }
2103
2104 if file_name.to_string_lossy().starts_with('.') {
2106 continue;
2107 }
2108
2109 if !src_path.exists() {
2111 continue;
2112 }
2113
2114 if src_path.is_dir() {
2115 copy_dir_recursive(&src_path, &dst_path)?;
2116 } else if file_name == "Cargo.toml" {
2117 match fs::read_to_string(&src_path) {
2120 Ok(content) => {
2121 let filtered: String = content
2122 .lines()
2123 .filter(|line| !line.trim().starts_with("[workspace]"))
2124 .collect::<Vec<_>>()
2125 .join("\n");
2126 fs::write(&dst_path, filtered)
2127 .map_err(|e| CompileError::Io(e.to_string()))?;
2128 }
2129 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
2130 Err(e) => return Err(CompileError::Io(e.to_string())),
2131 }
2132 } else {
2133 match fs::copy(&src_path, &dst_path) {
2134 Ok(_) => {}
2135 Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
2136 Err(e) => return Err(CompileError::Io(e.to_string())),
2137 }
2138 }
2139 }
2140
2141 Ok(())
2142}
2143
2144pub fn compile_and_run(source: &str, output_dir: &Path) -> Result<String, CompileError> {
2187 compile_to_rust_checked(source).map_err(CompileError::Parse)?;
2190
2191 compile_to_dir(source, output_dir)?;
2192
2193 let build_output = Command::new("cargo")
2195 .arg("build")
2196 .arg("--message-format=json")
2197 .current_dir(output_dir)
2198 .output()
2199 .map_err(|e| CompileError::Io(e.to_string()))?;
2200
2201 if !build_output.status.success() {
2202 let stderr = String::from_utf8_lossy(&build_output.stderr);
2203 let stdout = String::from_utf8_lossy(&build_output.stdout);
2204
2205 let diagnostics = parse_rustc_json(&stdout);
2207
2208 if !diagnostics.is_empty() {
2209 let source_map = SourceMap::new(source.to_string());
2211 let interner = Interner::new();
2212
2213 if let Some(logos_error) = translate_diagnostics(&diagnostics, &source_map, &interner) {
2214 return Err(CompileError::Ownership(logos_error));
2215 }
2216 }
2217
2218 return Err(CompileError::Build(stderr.to_string()));
2220 }
2221
2222 let run_output = Command::new("cargo")
2224 .arg("run")
2225 .arg("--quiet")
2226 .current_dir(output_dir)
2227 .output()
2228 .map_err(|e| CompileError::Io(e.to_string()))?;
2229
2230 if !run_output.status.success() {
2231 let stderr = String::from_utf8_lossy(&run_output.stderr);
2232 return Err(CompileError::Runtime(stderr.to_string()));
2233 }
2234
2235 let stdout = String::from_utf8_lossy(&run_output.stdout);
2236 Ok(stdout.to_string())
2237}
2238
2239pub fn compile_file(path: &Path) -> Result<String, CompileError> {
2242 let source = fs::read_to_string(path).map_err(|e| CompileError::Io(e.to_string()))?;
2243 compile_to_rust(&source).map_err(CompileError::Parse)
2244}
2245
2246pub fn compile_project(entry_file: &Path) -> Result<CompileOutput, CompileError> {
2264 use crate::loader::Loader;
2265 use crate::analysis::discover_with_imports;
2266
2267 let root_path = entry_file.parent().unwrap_or(Path::new(".")).to_path_buf();
2268 let mut loader = Loader::new(root_path);
2269 let mut interner = Interner::new();
2270
2271 let raw_source = fs::read_to_string(entry_file)
2273 .map_err(|e| CompileError::Io(format!("Failed to read entry file: {}", e)))?;
2274
2275 let source = crate::loader::apply_prelude(&raw_source).into_owned();
2280
2281 let type_registry = discover_with_imports(entry_file, &source, &mut loader, &mut interner)
2283 .map_err(|e| CompileError::Io(e))?;
2284
2285 compile_to_rust_with_registry_full(&source, type_registry, &mut interner)
2287 .map_err(CompileError::Parse)
2288}
2289
2290fn compile_to_rust_with_registry_full(
2293 source: &str,
2294 type_registry: crate::analysis::TypeRegistry,
2295 interner: &mut Interner,
2296) -> Result<CompileOutput, ParseError> {
2297 let mut lexer = Lexer::new(source, interner);
2298 let tokens = lexer.tokenize();
2299
2300 let policy_registry = {
2302 let mut discovery = DiscoveryPass::new(&tokens, interner);
2303 discovery.run_full().policies
2304 };
2305
2306 let codegen_registry = type_registry.clone();
2307 let codegen_policies = policy_registry.clone();
2308
2309 let mut world_state = WorldState::new();
2310 let expr_arena = Arena::new();
2311 let term_arena = Arena::new();
2312 let np_arena = Arena::new();
2313 let sym_arena = Arena::new();
2314 let role_arena = Arena::new();
2315 let pp_arena = Arena::new();
2316 let stmt_arena: Arena<Stmt> = Arena::new();
2317 let imperative_expr_arena: Arena<Expr> = Arena::new();
2318 let type_expr_arena: Arena<TypeExpr> = Arena::new();
2319
2320 let ast_ctx = AstContext::with_types(
2321 &expr_arena,
2322 &term_arena,
2323 &np_arena,
2324 &sym_arena,
2325 &role_arena,
2326 &pp_arena,
2327 &stmt_arena,
2328 &imperative_expr_arena,
2329 &type_expr_arena,
2330 );
2331
2332 let mut parser = Parser::new(tokens, &mut world_state, interner, ast_ctx, type_registry);
2333 let stmts = parser.parse_program()?;
2334
2335 let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, interner, &crate::optimization::OptimizationConfig::from_env());
2344
2345 let mut dependencies = extract_dependencies(&stmts, interner)?;
2347
2348 let needs_wasm_bindgen = stmts.iter().any(|stmt| {
2350 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
2351 interner.resolve(*target).eq_ignore_ascii_case("wasm")
2352 } else {
2353 false
2354 }
2355 });
2356 if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
2357 dependencies.push(CrateDependency {
2358 name: "wasm-bindgen".to_string(),
2359 version: "0.2".to_string(),
2360 features: vec![],
2361 });
2362 }
2363
2364 let mut escape_checker = EscapeChecker::new(interner);
2365 escape_checker.check_program(&stmts).map_err(|e| {
2366 ParseError {
2367 kind: crate::error::ParseErrorKind::Custom(e.to_string()),
2368 span: e.span,
2369 }
2370 })?;
2371
2372 let type_env = crate::analysis::check_program(&stmts, interner, &codegen_registry)
2373 .map_err(|e| ParseError {
2374 kind: e.to_parse_error_kind(interner),
2375 span: crate::token::Span::default(),
2376 })?;
2377 let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, interner, &type_env, &crate::optimization::OptimizationConfig::from_env());
2378
2379 let has_c = stmts.iter().any(|stmt| {
2381 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
2382 match export_target {
2383 None => true,
2384 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2385 }
2386 } else {
2387 false
2388 }
2389 });
2390
2391 let c_header = if has_c {
2392 Some(generate_c_header(&stmts, "module", interner, &codegen_registry))
2393 } else {
2394 None
2395 };
2396
2397 if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
2398 dependencies.push(CrateDependency {
2399 name: "serde_json".to_string(),
2400 version: "1".to_string(),
2401 features: vec![],
2402 });
2403 }
2404
2405 let python_bindings = if has_c {
2406 Some(generate_python_bindings(&stmts, "module", interner, &codegen_registry))
2407 } else {
2408 None
2409 };
2410
2411 let (typescript_bindings, typescript_types) = if has_c {
2412 let (js, dts) = generate_typescript_bindings(&stmts, "module", interner, &codegen_registry);
2413 (Some(js), Some(dts))
2414 } else {
2415 (None, None)
2416 };
2417
2418 Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
2419}
2420
2421#[derive(Debug)]
2443pub enum CompileError {
2444 Parse(ParseError),
2449
2450 Io(String),
2454
2455 Build(String),
2460
2461 Runtime(String),
2465
2466 Ownership(LogosError),
2472}
2473
2474impl std::fmt::Display for CompileError {
2475 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2476 match self {
2477 CompileError::Parse(e) => write!(f, "Parse error: {:?}", e),
2478 CompileError::Io(e) => write!(f, "IO error: {}", e),
2479 CompileError::Build(e) => write!(f, "Build error: {}", e),
2480 CompileError::Runtime(e) => write!(f, "Runtime error: {}", e),
2481 CompileError::Ownership(e) => write!(f, "{}", e),
2482 }
2483 }
2484}
2485
2486impl std::error::Error for CompileError {}
2487
2488pub fn encode_program_source(source: &str) -> Result<String, ParseError> {
2498 let full_source = if source.contains("## Main") || source.contains("## To ") {
2499 source.to_string()
2500 } else {
2501 format!("## Main\n{}", source)
2502 };
2503
2504 let mut interner = Interner::new();
2505 let mut lexer = Lexer::new(&full_source, &mut interner);
2506 let tokens = lexer.tokenize();
2507
2508 let type_registry = {
2509 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
2510 let result = discovery.run_full();
2511 result.types
2512 };
2513
2514 let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
2516 for (_type_name, type_def) in type_registry.iter_types() {
2517 if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
2518 for variant in variants {
2519 let vname = interner.resolve(variant.name).to_string();
2520 let field_names: Vec<String> = variant.fields.iter()
2521 .map(|f| interner.resolve(f.name).to_string())
2522 .collect();
2523 variant_constructors.insert(vname, field_names);
2524 }
2525 }
2526 }
2527
2528 let mut world_state = WorldState::new();
2529 let expr_arena = Arena::new();
2530 let term_arena = Arena::new();
2531 let np_arena = Arena::new();
2532 let sym_arena = Arena::new();
2533 let role_arena = Arena::new();
2534 let pp_arena = Arena::new();
2535 let stmt_arena: Arena<Stmt> = Arena::new();
2536 let imperative_expr_arena: Arena<Expr> = Arena::new();
2537 let type_expr_arena: Arena<TypeExpr> = Arena::new();
2538
2539 let ast_ctx = AstContext::with_types(
2540 &expr_arena, &term_arena, &np_arena, &sym_arena,
2541 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
2542 &type_expr_arena,
2543 );
2544
2545 let mut parser = crate::parser::Parser::new(
2546 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
2547 );
2548 let stmts = parser.parse_program()?;
2549
2550 let mut functions: Vec<(String, Vec<String>, Vec<String>, String, Vec<&Stmt>)> = Vec::new();
2551 let mut main_stmts: Vec<&Stmt> = Vec::new();
2552
2553 for stmt in &stmts {
2554 if let Stmt::FunctionDef { name, params, body, return_type, is_native, .. } = stmt {
2555 if *is_native {
2556 continue; }
2558 let fn_name = interner.resolve(*name).to_string();
2559 let param_names: Vec<String> = params
2560 .iter()
2561 .map(|(name, _)| interner.resolve(*name).to_string())
2562 .collect();
2563 let param_types: Vec<String> = params
2564 .iter()
2565 .map(|(_, ty)| decompile_type_expr(ty, &interner))
2566 .collect();
2567 let ret_type = return_type
2568 .map(|rt| decompile_type_expr(rt, &interner))
2569 .unwrap_or_else(|| "Nothing".to_string());
2570 let body_stmts: Vec<&Stmt> = body.iter().collect();
2571 functions.push((fn_name, param_names, param_types, ret_type, body_stmts));
2572 } else {
2573 main_stmts.push(stmt);
2574 }
2575 }
2576
2577 let mut counter = 0usize;
2578 let mut output = String::new();
2579 reset_inspect_otherwise_idx(); output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
2585 output.push_str("Let encodedFuncSeq be a new Seq of CFunc.\n");
2586
2587 for (fn_name, params, param_types, ret_type, body) in &functions {
2588 let body_var = encode_stmt_list_src(body, &mut counter, &mut output, &interner, &variant_constructors);
2589
2590 let params_var = format!("params_{}", counter);
2591 counter += 1;
2592 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2593 for p in params {
2594 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
2595 }
2596
2597 let param_types_var = format!("paramTypes_{}", counter);
2598 counter += 1;
2599 output.push_str(&format!("Let {} be a new Seq of Text.\n", param_types_var));
2600 for pt in param_types {
2601 output.push_str(&format!("Push \"{}\" to {}.\n", pt, param_types_var));
2602 }
2603
2604 let func_var = format!("func_{}", counter);
2605 counter += 1;
2606 output.push_str(&format!(
2607 "Let {} be a new CFuncDef with name \"{}\" and params {} and paramTypes {} and returnType \"{}\" and body {}.\n",
2608 func_var, fn_name, params_var, param_types_var, ret_type, body_var
2609 ));
2610 output.push_str(&format!("Push copy of {} to encodedFuncSeq.\n", func_var));
2615 output.push_str(&format!(
2616 "Set item \"{}\" of encodedFuncMap to {}.\n",
2617 fn_name, func_var
2618 ));
2619 }
2620
2621 let main_var = encode_stmt_list_src(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
2623 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
2624
2625 Ok(output)
2626}
2627
2628pub fn encode_program_source_compact(source: &str) -> Result<String, ParseError> {
2632 let full_source = if source.contains("## Main") || source.contains("## To ") {
2633 source.to_string()
2634 } else {
2635 format!("## Main\n{}", source)
2636 };
2637
2638 let mut interner = Interner::new();
2639 let mut lexer = Lexer::new(&full_source, &mut interner);
2640 let tokens = lexer.tokenize();
2641
2642 let type_registry = {
2643 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
2644 let result = discovery.run_full();
2645 result.types
2646 };
2647
2648 let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
2649 for (_type_name, type_def) in type_registry.iter_types() {
2650 if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
2651 for variant in variants {
2652 let vname = interner.resolve(variant.name).to_string();
2653 let field_names: Vec<String> = variant.fields.iter()
2654 .map(|f| interner.resolve(f.name).to_string())
2655 .collect();
2656 variant_constructors.insert(vname, field_names);
2657 }
2658 }
2659 }
2660
2661 let mut world_state = WorldState::new();
2662 let expr_arena = Arena::new();
2663 let term_arena = Arena::new();
2664 let np_arena = Arena::new();
2665 let sym_arena = Arena::new();
2666 let role_arena = Arena::new();
2667 let pp_arena = Arena::new();
2668 let stmt_arena: Arena<Stmt> = Arena::new();
2669 let imperative_expr_arena: Arena<Expr> = Arena::new();
2670 let type_expr_arena: Arena<TypeExpr> = Arena::new();
2671
2672 let ast_ctx = AstContext::with_types(
2673 &expr_arena, &term_arena, &np_arena, &sym_arena,
2674 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
2675 &type_expr_arena,
2676 );
2677
2678 let mut parser = crate::parser::Parser::new(
2679 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
2680 );
2681 let stmts = parser.parse_program()?;
2682
2683 let mut functions: Vec<(String, Vec<String>, Vec<String>, String, Vec<&Stmt>)> = Vec::new();
2684 let mut main_stmts: Vec<&Stmt> = Vec::new();
2685
2686 for stmt in &stmts {
2687 if let Stmt::FunctionDef { name, params, body, return_type, is_native, .. } = stmt {
2688 if *is_native { continue; }
2689 let fn_name = interner.resolve(*name).to_string();
2690 let param_names: Vec<String> = params
2691 .iter()
2692 .map(|(name, _)| interner.resolve(*name).to_string())
2693 .collect();
2694 let param_types: Vec<String> = params
2695 .iter()
2696 .map(|(_, ty)| decompile_type_expr(ty, &interner))
2697 .collect();
2698 let ret_type = return_type
2699 .map(|rt| decompile_type_expr(rt, &interner))
2700 .unwrap_or_else(|| "Nothing".to_string());
2701 let body_stmts: Vec<&Stmt> = body.iter().collect();
2702 functions.push((fn_name, param_names, param_types, ret_type, body_stmts));
2703 } else {
2704 main_stmts.push(stmt);
2705 }
2706 }
2707
2708 let mut counter = 0usize;
2709 let mut output = String::new();
2710
2711 output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
2712
2713 for (fn_name, params, param_types, ret_type, body) in &functions {
2714 let body_var = encode_stmt_list_compact(body, &mut counter, &mut output, &interner, &variant_constructors);
2715
2716 let params_var = format!("params_{}", counter);
2717 counter += 1;
2718 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2719 for p in params {
2720 output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
2721 }
2722
2723 let param_types_var = format!("paramTypes_{}", counter);
2724 counter += 1;
2725 output.push_str(&format!("Let {} be a new Seq of Text.\n", param_types_var));
2726 for pt in param_types {
2727 output.push_str(&format!("Push \"{}\" to {}.\n", pt, param_types_var));
2728 }
2729
2730 let func_var = format!("func_{}", counter);
2731 counter += 1;
2732 output.push_str(&format!(
2733 "Let {} be a new CFuncDef with name \"{}\" and params {} and paramTypes {} and returnType \"{}\" and body {}.\n",
2734 func_var, fn_name, params_var, param_types_var, ret_type, body_var
2735 ));
2736 output.push_str(&format!(
2737 "Set item \"{}\" of encodedFuncMap to {}.\n",
2738 fn_name, func_var
2739 ));
2740 }
2741
2742 let main_var = encode_stmt_list_compact(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
2743 output.push_str(&format!("Let encodedMain be {}.\n", main_var));
2744
2745 Ok(output)
2746}
2747
2748fn try_inline_expr(expr: &Expr, interner: &Interner) -> Option<String> {
2751 match expr {
2752 Expr::Literal(lit) => match lit {
2753 Literal::Number(n) => Some(format!("(a new CInt with value {})", n)),
2754 Literal::Boolean(b) => Some(format!("(a new CBool with value {})", b)),
2755 Literal::Text(s) => {
2756 let text = interner.resolve(*s);
2757 Some(format!("(a new CText with value \"{}\")", text))
2758 }
2759 Literal::Float(f) => {
2760 let fs = format!("{}", f);
2761 let fs = if fs.contains('.') { fs } else { format!("{}.0", fs) };
2762 Some(format!("(a new CFloat with value {})", fs))
2763 }
2764 Literal::Nothing => Some("(a new CText with value \"nothing\")".to_string()),
2765 _ => None,
2766 },
2767 Expr::Identifier(sym) => {
2768 let name = interner.resolve(*sym);
2769 Some(format!("(a new CVar with name \"{}\")", name))
2770 }
2771 Expr::Not { operand } => {
2772 if let Some(inner) = try_inline_expr(operand, interner) {
2773 Some(format!("(a new CNot with inner {})", inner))
2774 } else {
2775 None
2776 }
2777 }
2778 Expr::OptionNone => Some("(a new COptionNone)".to_string()),
2779 _ => None,
2780 }
2781}
2782
2783fn encode_expr_compact(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2784 if let Some(inline) = try_inline_expr(expr, interner) {
2786 return inline;
2787 }
2788
2789 let var = format!("e_{}", *counter);
2791 *counter += 1;
2792
2793 match expr {
2794 Expr::BinaryOp { op, left, right } => {
2795 let left_var = encode_expr_compact(left, counter, output, interner, variants);
2796 let right_var = encode_expr_compact(right, counter, output, interner, variants);
2797 let op_str = match op {
2798 BinaryOpKind::Add => "+",
2799 BinaryOpKind::Subtract => "-",
2800 BinaryOpKind::Multiply => "*",
2801 BinaryOpKind::Divide | BinaryOpKind::ExactDivide => "/",
2802 BinaryOpKind::FloorDivide => "//",
2803 BinaryOpKind::Modulo => "%",
2804 BinaryOpKind::Eq => "==",
2805 BinaryOpKind::NotEq => "!=",
2806 BinaryOpKind::Lt => "<",
2807 BinaryOpKind::Gt => ">",
2808 BinaryOpKind::LtEq => "<=",
2809 BinaryOpKind::GtEq => ">=",
2810 BinaryOpKind::And => "&&",
2811 BinaryOpKind::Or => "||",
2812 BinaryOpKind::Concat => "+",
2813 BinaryOpKind::SeqConcat => "followed by",
2814 BinaryOpKind::ApproxEq => "is approximately",
2815 BinaryOpKind::Pow => "**",
2816 BinaryOpKind::BitXor => "^",
2817 BinaryOpKind::BitAnd => "&",
2818 BinaryOpKind::BitOr => "|",
2819 BinaryOpKind::Shl => "<<",
2820 BinaryOpKind::Shr => ">>",
2821 };
2822 output.push_str(&format!(
2823 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
2824 var, op_str, left_var, right_var
2825 ));
2826 }
2827 Expr::Call { function, args } => {
2828 let fn_name = interner.resolve(*function);
2829 if let Some(field_names) = variants.get(fn_name) {
2830 let names_var = format!("nvNames_{}", *counter);
2831 *counter += 1;
2832 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2833 let vals_var = format!("nvVals_{}", *counter);
2834 *counter += 1;
2835 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2836 for (i, arg) in args.iter().enumerate() {
2837 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
2838 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2839 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
2840 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
2841 }
2842 output.push_str(&format!(
2843 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2844 var, fn_name, names_var, vals_var
2845 ));
2846 } else {
2847 let args_var = format!("callArgs_{}", *counter);
2848 *counter += 1;
2849 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2850 for arg in args {
2851 let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
2852 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2853 }
2854 output.push_str(&format!(
2855 "Let {} be a new CCall with name \"{}\" and args {}.\n",
2856 var, fn_name, args_var
2857 ));
2858 }
2859 }
2860 Expr::Index { collection, index } => {
2861 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
2862 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
2863 output.push_str(&format!(
2864 "Let {} be a new CIndex with coll {} and idx {}.\n",
2865 var, coll_var, idx_var
2866 ));
2867 }
2868 Expr::Length { collection } => {
2869 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
2870 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
2871 }
2872 Expr::FieldAccess { object, field } => {
2873 let obj_var = encode_expr_compact(object, counter, output, interner, variants);
2874 let field_name = interner.resolve(*field);
2875 output.push_str(&format!(
2876 "Let {} be a new CMapGet with target {} and key (a new CText with value \"{}\").\n",
2877 var, obj_var, field_name
2878 ));
2879 }
2880 Expr::NewVariant { variant, fields, .. } => {
2881 let variant_name = interner.resolve(*variant);
2882 let names_var = format!("nvNames_{}", *counter);
2883 *counter += 1;
2884 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2885 let vals_var = format!("nvVals_{}", *counter);
2886 *counter += 1;
2887 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2888 for (field_name, field_expr) in fields {
2889 let fname = interner.resolve(*field_name);
2890 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2891 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
2892 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2893 }
2894 output.push_str(&format!(
2895 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2896 var, variant_name, names_var, vals_var
2897 ));
2898 }
2899 Expr::New { type_name, init_fields, .. } => {
2900 let tn = interner.resolve(*type_name);
2901 if tn == "Seq" || tn == "List" {
2902 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
2903 } else if tn == "Set" {
2904 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
2905 } else {
2906 let names_var = format!("nvNames_{}", *counter);
2907 *counter += 1;
2908 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2909 let vals_var = format!("nvVals_{}", *counter);
2910 *counter += 1;
2911 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2912 for (field_name, field_expr) in init_fields {
2913 let fname = interner.resolve(*field_name);
2914 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2915 let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
2916 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2917 }
2918 output.push_str(&format!(
2919 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2920 var, tn, names_var, vals_var
2921 ));
2922 }
2923 }
2924 Expr::InterpolatedString(parts) => {
2925 if parts.is_empty() {
2926 output.push_str(&format!("Let {} be (a new CText with value \"\").\n", var));
2927 } else {
2928 let parts_var = format!("isparts_{}", *counter);
2931 *counter += 1;
2932 output.push_str(&format!("Let {} be a new Seq of CStringPart.\n", parts_var));
2933 for part in parts {
2934 match part {
2935 StringPart::Literal(sym) => {
2936 let text = interner.resolve(*sym);
2937 output.push_str(&format!(
2938 "Push a new CLiteralPart with value \"{}\" to {}.\n", text, parts_var
2939 ));
2940 }
2941 StringPart::Expr { value, .. } => {
2942 let pv = encode_expr_compact(value, counter, output, interner, variants);
2943 output.push_str(&format!(
2944 "Push a new CExprPart with expr {} to {}.\n", pv, parts_var
2945 ));
2946 }
2947 }
2948 }
2949 output.push_str(&format!(
2950 "Let {} be a new CInterpolatedString with parts {}.\n", var, parts_var
2951 ));
2952 }
2953 }
2954 Expr::Range { start, end } => {
2955 let start_var = encode_expr_compact(start, counter, output, interner, variants);
2956 let end_var = encode_expr_compact(end, counter, output, interner, variants);
2957 output.push_str(&format!(
2958 "Let {} be a new CRange with start {} and end {}.\n",
2959 var, start_var, end_var
2960 ));
2961 }
2962 Expr::Copy { expr } => {
2963 let inner_var = encode_expr_compact(expr, counter, output, interner, variants);
2964 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
2965 }
2966 Expr::Contains { collection, value } => {
2967 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
2968 let val_var = encode_expr_compact(value, counter, output, interner, variants);
2969 output.push_str(&format!(
2970 "Let {} be a new CContains with coll {} and elem {}.\n",
2971 var, coll_var, val_var
2972 ));
2973 }
2974 Expr::OptionSome { value } => {
2975 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
2976 output.push_str(&format!(
2977 "Let {} be a new COptionSome with inner {}.\n", var, inner_var
2978 ));
2979 }
2980 Expr::Tuple(elems) => {
2981 let items_var = format!("tupItems_{}", *counter);
2982 *counter += 1;
2983 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2984 for elem in elems {
2985 let elem_var = encode_expr_compact(elem, counter, output, interner, variants);
2986 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2987 }
2988 output.push_str(&format!(
2989 "Let {} be a new CTuple with items {}.\n", var, items_var
2990 ));
2991 }
2992 Expr::Closure { params, body, .. } => {
2993 let params_var = format!("clp_{}", *counter);
2994 *counter += 1;
2995 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2996 let mut param_names = HashSet::new();
2997 for (sym, _) in params {
2998 let name = interner.resolve(*sym);
2999 param_names.insert(name.to_string());
3000 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
3001 }
3002 let body_var = format!("clb_{}", *counter);
3003 *counter += 1;
3004 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
3005 match body {
3006 ClosureBody::Expression(e) => {
3007 let ret_expr = encode_expr_compact(e, counter, output, interner, variants);
3008 let ret_var = format!("s_{}", *counter);
3009 *counter += 1;
3010 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
3011 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
3012 }
3013 ClosureBody::Block(stmts) => {
3014 for s in stmts.iter() {
3015 let sv = encode_stmt_compact(s, counter, output, interner, variants);
3016 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
3017 }
3018 }
3019 }
3020 let bound: HashSet<String> = param_names;
3021 let mut free: Vec<String> = collect_free_vars_expr(expr, interner, &bound).into_iter().collect();
3026 free.sort();
3027 let cap_var = format!("clc_{}", *counter);
3028 *counter += 1;
3029 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
3030 for fv in &free {
3031 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
3032 }
3033 output.push_str(&format!(
3034 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
3035 var, params_var, body_var, cap_var
3036 ));
3037 }
3038 Expr::CallExpr { callee, args } => {
3039 let callee_var = encode_expr_compact(callee, counter, output, interner, variants);
3040 let args_var = format!("cea_{}", *counter);
3041 *counter += 1;
3042 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
3043 for a in args {
3044 let av = encode_expr_compact(a, counter, output, interner, variants);
3045 output.push_str(&format!("Push {} to {}.\n", av, args_var));
3046 }
3047 output.push_str(&format!(
3048 "Let {} be a new CCallExpr with target {} and args {}.\n",
3049 var, callee_var, args_var
3050 ));
3051 }
3052 Expr::Slice { collection, start, end } => {
3053 let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
3054 let start_var = encode_expr_compact(start, counter, output, interner, variants);
3055 let end_var = encode_expr_compact(end, counter, output, interner, variants);
3056 output.push_str(&format!(
3057 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
3058 var, coll_var, start_var, end_var
3059 ));
3060 }
3061 Expr::Union { left, right } => {
3062 let left_var = encode_expr_compact(left, counter, output, interner, variants);
3063 let right_var = encode_expr_compact(right, counter, output, interner, variants);
3064 output.push_str(&format!(
3065 "Let {} be a new CUnion with left {} and right {}.\n",
3066 var, left_var, right_var
3067 ));
3068 }
3069 Expr::Intersection { left, right } => {
3070 let left_var = encode_expr_compact(left, counter, output, interner, variants);
3071 let right_var = encode_expr_compact(right, counter, output, interner, variants);
3072 output.push_str(&format!(
3073 "Let {} be a new CIntersection with left {} and right {}.\n",
3074 var, left_var, right_var
3075 ));
3076 }
3077 Expr::Give { value } => {
3078 let inner_var = encode_expr_compact(value, counter, output, interner, variants);
3079 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
3080 }
3081 Expr::Escape { code, .. } => {
3082 let code_str = interner.resolve(*code);
3083 output.push_str(&format!(
3084 "Let {} be a new CEscExpr with code \"{}\".\n",
3085 var, code_str.replace('\"', "\\\"")
3086 ));
3087 }
3088 _ => {
3089 output.push_str(&format!("Let {} be (a new CText with value \"unsupported\").\n", var));
3091 }
3092 }
3093
3094 var
3095}
3096
3097fn encode_stmt_compact(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3098 let var = format!("s_{}", *counter);
3099 *counter += 1;
3100
3101 match stmt {
3102 Stmt::Let { var: name, value, .. } => {
3103 let name_str = interner.resolve(*name);
3104 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
3105 output.push_str(&format!(
3106 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3107 var, name_str, expr_var
3108 ));
3109 }
3110 Stmt::Set { target, value } => {
3111 let name_str = interner.resolve(*target);
3112 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
3113 output.push_str(&format!(
3114 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3115 var, name_str, expr_var
3116 ));
3117 }
3118 Stmt::If { cond, then_block, else_block } => {
3119 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
3120 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
3121 let then_var = encode_stmt_list_compact(&then_stmts, counter, output, interner, variants);
3122 let else_var = if let Some(els) = else_block {
3123 let else_stmts: Vec<&Stmt> = els.iter().collect();
3124 encode_stmt_list_compact(&else_stmts, counter, output, interner, variants)
3125 } else {
3126 let empty_var = format!("emptyBlock_{}", *counter);
3127 *counter += 1;
3128 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
3129 empty_var
3130 };
3131 output.push_str(&format!(
3132 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3133 var, cond_var, then_var, else_var
3134 ));
3135 }
3136 Stmt::While { cond, body, .. } => {
3137 let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
3138 let body_stmts: Vec<&Stmt> = body.iter().collect();
3139 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
3140 output.push_str(&format!(
3141 "Let {} be a new CWhile with cond {} and body {}.\n",
3142 var, cond_var, body_var
3143 ));
3144 }
3145 Stmt::Return { value } => {
3146 if let Some(val) = value {
3147 let expr_var = encode_expr_compact(val, counter, output, interner, variants);
3148 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
3149 } else {
3150 output.push_str(&format!("Let {} be a new CReturn with expr (a new CText with value \"nothing\").\n", var));
3151 }
3152 }
3153 Stmt::Show { object, .. } => {
3154 let expr_var = encode_expr_compact(object, counter, output, interner, variants);
3155 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
3156 }
3157 Stmt::Repeat { pattern, iterable, body } => {
3158 let var_str = match pattern {
3159 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3160 Pattern::Tuple(syms) => {
3161 if let Some(s) = syms.first() {
3162 interner.resolve(*s).to_string()
3163 } else {
3164 "item".to_string()
3165 }
3166 }
3167 };
3168 let coll_var = encode_expr_compact(iterable, counter, output, interner, variants);
3169 let body_stmts: Vec<&Stmt> = body.iter().collect();
3170 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
3171 output.push_str(&format!(
3172 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
3173 var, var_str, coll_var, body_var
3174 ));
3175 }
3176 Stmt::Push { value, collection } => {
3177 let coll_name = extract_ident_name(collection, interner);
3178 let expr_var = encode_expr_compact(value, counter, output, interner, variants);
3179 output.push_str(&format!(
3180 "Let {} be a new CPush with expr {} and target \"{}\".\n",
3181 var, expr_var, coll_name
3182 ));
3183 }
3184 Stmt::SetIndex { collection, index, value } => {
3185 let target_str = extract_ident_name(collection, interner);
3186 let idx_var = encode_expr_compact(index, counter, output, interner, variants);
3187 let val_var = encode_expr_compact(value, counter, output, interner, variants);
3188 output.push_str(&format!(
3189 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
3190 var, target_str, idx_var, val_var
3191 ));
3192 }
3193 Stmt::SetField { object, field, value } => {
3194 let target_str = extract_ident_name(object, interner);
3195 let field_str = interner.resolve(*field);
3196 let val_var = encode_expr_compact(value, counter, output, interner, variants);
3197 output.push_str(&format!(
3198 "Let {} be a new CSetField with target \"{}\" and field \"{}\" and val {}.\n",
3199 var, target_str, field_str, val_var
3200 ));
3201 }
3202 Stmt::Break => {
3203 output.push_str(&format!("Let {} be a new CBreak.\n", var));
3204 }
3205 Stmt::Inspect { target, arms, .. } => {
3206 let target_var = encode_expr_compact(target, counter, output, interner, variants);
3207 let arms_var = format!("arms_{}", *counter);
3208 *counter += 1;
3209 output.push_str(&format!("Let {} be a new Seq of CMatchArm.\n", arms_var));
3210 for arm in arms {
3211 if let Some(variant_sym) = arm.variant {
3212 let vname = interner.resolve(variant_sym);
3213 let bindings_var = format!("bindings_{}", *counter);
3214 *counter += 1;
3215 output.push_str(&format!("Let {} be a new Seq of Text.\n", bindings_var));
3216 for (_, binding_name) in &arm.bindings {
3217 let bn = interner.resolve(*binding_name);
3218 output.push_str(&format!("Push \"{}\" to {}.\n", bn, bindings_var));
3219 }
3220 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
3221 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
3222 let arm_var = format!("arm_{}", *counter);
3223 *counter += 1;
3224 output.push_str(&format!(
3225 "Let {} be a new CWhen with variantName \"{}\" and bindings {} and body {}.\n",
3226 arm_var, vname, bindings_var, body_var
3227 ));
3228 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
3229 } else {
3230 let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
3231 let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
3232 let arm_var = format!("arm_{}", *counter);
3233 *counter += 1;
3234 output.push_str(&format!(
3235 "Let {} be a new COtherwise with body {}.\n",
3236 arm_var, body_var
3237 ));
3238 output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
3239 }
3240 }
3241 output.push_str(&format!(
3242 "Let {} be a new CInspect with target {} and arms {}.\n",
3243 var, target_var, arms_var
3244 ));
3245 }
3246 _ => {
3247 return encode_stmt_src(stmt, counter, output, interner, variants);
3249 }
3250 }
3251
3252 var
3253}
3254
3255fn encode_stmt_list_compact(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3256 let list_var = format!("stmts_{}", *counter);
3257 *counter += 1;
3258 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
3259 for s in stmts {
3260 let sv = encode_stmt_compact(s, counter, output, interner, variants);
3261 output.push_str(&format!("Push {} to {}.\n", sv, list_var));
3262 }
3263 list_var
3264}
3265
3266fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
3267 let mut free = HashSet::new();
3268 match expr {
3269 Expr::Identifier(sym) => {
3270 let name = interner.resolve(*sym).to_string();
3271 if !bound.contains(&name) {
3272 free.insert(name);
3273 }
3274 }
3275 Expr::BinaryOp { left, right, .. } => {
3276 free.extend(collect_free_vars_expr(left, interner, bound));
3277 free.extend(collect_free_vars_expr(right, interner, bound));
3278 }
3279 Expr::Not { operand } => {
3280 free.extend(collect_free_vars_expr(operand, interner, bound));
3281 }
3282 Expr::Copy { expr: inner } => {
3283 free.extend(collect_free_vars_expr(inner, interner, bound));
3284 }
3285 Expr::CallExpr { callee, args } => {
3286 free.extend(collect_free_vars_expr(callee, interner, bound));
3287 for a in args {
3288 free.extend(collect_free_vars_expr(a, interner, bound));
3289 }
3290 }
3291 Expr::Index { collection, index } => {
3292 free.extend(collect_free_vars_expr(collection, interner, bound));
3293 free.extend(collect_free_vars_expr(index, interner, bound));
3294 }
3295 Expr::InterpolatedString(parts) => {
3296 for part in parts {
3297 if let StringPart::Expr { value, .. } = part {
3298 free.extend(collect_free_vars_expr(value, interner, bound));
3299 }
3300 }
3301 }
3302 Expr::Closure { params, body, .. } => {
3303 let mut inner_bound = bound.clone();
3304 for (sym, _) in params {
3305 inner_bound.insert(interner.resolve(*sym).to_string());
3306 }
3307 match body {
3308 ClosureBody::Expression(e) => {
3309 free.extend(collect_free_vars_expr(e, interner, &inner_bound));
3310 }
3311 ClosureBody::Block(stmts) => {
3312 for s in stmts.iter() {
3313 free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
3314 }
3315 }
3316 }
3317 }
3318 _ => {}
3319 }
3320 free
3321}
3322
3323fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
3324 let mut free = HashSet::new();
3325 match stmt {
3326 Stmt::Let { var, value, .. } => {
3327 free.extend(collect_free_vars_expr(value, interner, bound));
3328 }
3329 Stmt::Set { target, value, .. } => {
3330 let n = interner.resolve(*target).to_string();
3331 if !bound.contains(&n) {
3332 free.insert(n);
3333 }
3334 free.extend(collect_free_vars_expr(value, interner, bound));
3335 }
3336 Stmt::Show { object, .. } => {
3337 free.extend(collect_free_vars_expr(object, interner, bound));
3338 }
3339 Stmt::Return { value } => {
3340 if let Some(v) = value {
3341 free.extend(collect_free_vars_expr(v, interner, bound));
3342 }
3343 }
3344 _ => {}
3345 }
3346 free
3347}
3348
3349fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3350 let var = format!("e_{}", *counter);
3351 *counter += 1;
3352
3353 match expr {
3354 Expr::Literal(lit) => match lit {
3355 Literal::Number(n) => {
3356 output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
3357 }
3358 Literal::Boolean(b) => {
3359 output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
3360 }
3361 Literal::Text(s) => {
3362 let text = interner.resolve(*s);
3363 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
3364 }
3365 Literal::Float(f) => {
3366 let fs = format!("{}", f);
3370 let fs = if fs.contains('.') || fs.contains('e') || fs.contains('E') {
3371 fs
3372 } else {
3373 format!("{}.0", fs)
3374 };
3375 output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, fs));
3376 }
3377 Literal::Duration(nanos) => {
3378 let millis = nanos / 1_000_000;
3379 let amount_var = format!("e_{}", *counter);
3380 *counter += 1;
3381 output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
3382 output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
3383 }
3384 Literal::Nothing => {
3385 output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
3386 }
3387 _ => {
3388 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
3389 }
3390 },
3391 Expr::Identifier(sym) => {
3392 let name = interner.resolve(*sym);
3393 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
3394 }
3395 Expr::BinaryOp { op, left, right } => {
3396 let left_var = encode_expr_src(left, counter, output, interner, variants);
3397 let right_var = encode_expr_src(right, counter, output, interner, variants);
3398 let op_str = match op {
3399 BinaryOpKind::Add => "+",
3400 BinaryOpKind::Subtract => "-",
3401 BinaryOpKind::Multiply => "*",
3402 BinaryOpKind::Divide | BinaryOpKind::ExactDivide => "/",
3403 BinaryOpKind::FloorDivide => "//",
3404 BinaryOpKind::Modulo => "%",
3405 BinaryOpKind::Eq => "==",
3406 BinaryOpKind::NotEq => "!=",
3407 BinaryOpKind::Lt => "<",
3408 BinaryOpKind::Gt => ">",
3409 BinaryOpKind::LtEq => "<=",
3410 BinaryOpKind::GtEq => ">=",
3411 BinaryOpKind::And => "&&",
3412 BinaryOpKind::Or => "||",
3413 BinaryOpKind::Concat => "+",
3414 BinaryOpKind::SeqConcat => "followed by",
3415 BinaryOpKind::ApproxEq => "is approximately",
3416 BinaryOpKind::Pow => "**",
3417 BinaryOpKind::BitXor => "^",
3418 BinaryOpKind::BitAnd => "&",
3419 BinaryOpKind::BitOr => "|",
3420 BinaryOpKind::Shl => "<<",
3421 BinaryOpKind::Shr => ">>",
3422 };
3423 output.push_str(&format!(
3424 "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
3425 var, op_str, left_var, right_var
3426 ));
3427 }
3428 Expr::Not { operand } => {
3429 let inner_var = encode_expr_src(operand, counter, output, interner, variants);
3430 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
3431 }
3432 Expr::Call { function, args } => {
3433 let fn_name = interner.resolve(*function);
3434 if let Some(field_names) = variants.get(fn_name) {
3435 let names_var = format!("nvNames_{}", *counter);
3437 *counter += 1;
3438 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
3439 let vals_var = format!("nvVals_{}", *counter);
3440 *counter += 1;
3441 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
3442 for (i, arg) in args.iter().enumerate() {
3443 let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
3444 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
3445 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
3446 output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
3447 }
3448 output.push_str(&format!(
3449 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
3450 var, fn_name, names_var, vals_var
3451 ));
3452 } else {
3453 let args_var = format!("callArgs_{}", *counter);
3454 *counter += 1;
3455 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
3456 for arg in args {
3457 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
3458 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
3459 }
3460 output.push_str(&format!(
3461 "Let {} be a new CCall with name \"{}\" and args {}.\n",
3462 var, fn_name, args_var
3463 ));
3464 }
3465 }
3466 Expr::Index { collection, index } => {
3467 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
3468 let idx_var = encode_expr_src(index, counter, output, interner, variants);
3469 output.push_str(&format!(
3470 "Let {} be a new CIndex with coll {} and idx {}.\n",
3471 var, coll_var, idx_var
3472 ));
3473 }
3474 Expr::Length { collection } => {
3475 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
3476 output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
3477 }
3478 Expr::FieldAccess { object, field } => {
3479 let obj_var = encode_expr_src(object, counter, output, interner, variants);
3480 let field_name = interner.resolve(*field);
3481 let key_var = format!("e_{}", *counter);
3482 *counter += 1;
3483 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
3484 output.push_str(&format!(
3485 "Let {} be a new CMapGet with target {} and key {}.\n",
3486 var, obj_var, key_var
3487 ));
3488 }
3489 Expr::NewVariant { variant, fields, .. } => {
3490 let variant_name = interner.resolve(*variant);
3491 let names_var = format!("nvNames_{}", *counter);
3492 *counter += 1;
3493 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
3494 let vals_var = format!("nvVals_{}", *counter);
3495 *counter += 1;
3496 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
3497 for (field_name, field_expr) in fields {
3498 let fname = interner.resolve(*field_name);
3499 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
3500 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
3501 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
3502 }
3503 output.push_str(&format!(
3504 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
3505 var, variant_name, names_var, vals_var
3506 ));
3507 }
3508 Expr::New { type_name, init_fields, .. } => {
3509 let tn = interner.resolve(*type_name);
3510 if tn == "Seq" || tn == "List" {
3511 output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
3512 } else if tn == "Set" {
3513 output.push_str(&format!("Let {} be a new CNewSet.\n", var));
3514 } else if tn == "Map" || tn.starts_with("Map ") {
3515 let names_var = format!("nvNames_{}", *counter);
3517 *counter += 1;
3518 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
3519 let vals_var = format!("nvVals_{}", *counter);
3520 *counter += 1;
3521 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
3522 output.push_str(&format!(
3523 "Let {} be a new CNew with typeName \"Map\" and fieldNames {} and fields {}.\n",
3524 var, names_var, vals_var
3525 ));
3526 } else if init_fields.is_empty() {
3527 let names_var = format!("nvNames_{}", *counter);
3528 *counter += 1;
3529 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
3530 let vals_var = format!("nvVals_{}", *counter);
3531 *counter += 1;
3532 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
3533 output.push_str(&format!(
3534 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
3535 var, tn, names_var, vals_var
3536 ));
3537 } else {
3538 let names_var = format!("nvNames_{}", *counter);
3539 *counter += 1;
3540 output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
3541 let vals_var = format!("nvVals_{}", *counter);
3542 *counter += 1;
3543 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
3544 for (field_name, field_expr) in init_fields {
3545 let fname = interner.resolve(*field_name);
3546 output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
3547 let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
3548 output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
3549 }
3550 output.push_str(&format!(
3551 "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
3552 var, tn, names_var, vals_var
3553 ));
3554 }
3555 }
3556 Expr::InterpolatedString(parts) => {
3557 if parts.is_empty() {
3558 output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
3559 } else {
3560 let parts_var = format!("isparts_{}", *counter);
3566 *counter += 1;
3567 output.push_str(&format!("Let {} be a new Seq of CStringPart.\n", parts_var));
3568 for part in parts {
3569 match part {
3570 StringPart::Literal(sym) => {
3571 let text = interner.resolve(*sym);
3572 output.push_str(&format!(
3573 "Push a new CLiteralPart with value \"{}\" to {}.\n", text, parts_var
3574 ));
3575 }
3576 StringPart::Expr { value, .. } => {
3577 let pv = encode_expr_src(value, counter, output, interner, variants);
3578 output.push_str(&format!(
3579 "Push a new CExprPart with expr {} to {}.\n", pv, parts_var
3580 ));
3581 }
3582 }
3583 }
3584 output.push_str(&format!(
3585 "Let {} be a new CInterpolatedString with parts {}.\n", var, parts_var
3586 ));
3587 }
3588 }
3589 Expr::Range { start, end } => {
3590 let start_var = encode_expr_src(start, counter, output, interner, variants);
3591 let end_var = encode_expr_src(end, counter, output, interner, variants);
3592 output.push_str(&format!(
3593 "Let {} be a new CRange with start {} and end {}.\n",
3594 var, start_var, end_var
3595 ));
3596 }
3597 Expr::Slice { collection, start, end } => {
3598 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
3599 let start_var = encode_expr_src(start, counter, output, interner, variants);
3600 let end_var = encode_expr_src(end, counter, output, interner, variants);
3601 output.push_str(&format!(
3602 "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
3603 var, coll_var, start_var, end_var
3604 ));
3605 }
3606 Expr::Copy { expr } => {
3607 let inner_var = encode_expr_src(expr, counter, output, interner, variants);
3608 output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
3609 }
3610 Expr::Contains { collection, value } => {
3611 let coll_var = encode_expr_src(collection, counter, output, interner, variants);
3612 let val_var = encode_expr_src(value, counter, output, interner, variants);
3613 output.push_str(&format!(
3614 "Let {} be a new CContains with coll {} and elem {}.\n",
3615 var, coll_var, val_var
3616 ));
3617 }
3618 Expr::Union { left, right } => {
3619 let left_var = encode_expr_src(left, counter, output, interner, variants);
3620 let right_var = encode_expr_src(right, counter, output, interner, variants);
3621 output.push_str(&format!(
3622 "Let {} be a new CUnion with left {} and right {}.\n",
3623 var, left_var, right_var
3624 ));
3625 }
3626 Expr::Intersection { left, right } => {
3627 let left_var = encode_expr_src(left, counter, output, interner, variants);
3628 let right_var = encode_expr_src(right, counter, output, interner, variants);
3629 output.push_str(&format!(
3630 "Let {} be a new CIntersection with left {} and right {}.\n",
3631 var, left_var, right_var
3632 ));
3633 }
3634 Expr::OptionSome { value } => {
3635 let inner_var = encode_expr_src(value, counter, output, interner, variants);
3636 output.push_str(&format!(
3637 "Let {} be a new COptionSome with inner {}.\n",
3638 var, inner_var
3639 ));
3640 }
3641 Expr::OptionNone => {
3642 output.push_str(&format!("Let {} be a new COptionNone.\n", var));
3643 }
3644 Expr::Tuple(elems) => {
3645 let items_var = format!("tupItems_{}", *counter);
3646 *counter += 1;
3647 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
3648 for elem in elems {
3649 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
3650 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
3651 }
3652 output.push_str(&format!(
3653 "Let {} be a new CTuple with items {}.\n",
3654 var, items_var
3655 ));
3656 }
3657 Expr::Closure { params, body, .. } => {
3658 let params_var = format!("clp_{}", *counter);
3659 *counter += 1;
3660 output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
3661 let mut param_names = HashSet::new();
3662 for (sym, _) in params {
3663 let name = interner.resolve(*sym);
3664 param_names.insert(name.to_string());
3665 output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
3666 }
3667 let body_var = format!("clb_{}", *counter);
3668 *counter += 1;
3669 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
3670 match body {
3671 ClosureBody::Expression(e) => {
3672 let ret_expr = encode_expr_src(e, counter, output, interner, variants);
3673 let ret_var = format!("s_{}", *counter);
3674 *counter += 1;
3675 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
3676 output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
3677 }
3678 ClosureBody::Block(stmts) => {
3679 for s in stmts.iter() {
3680 let sv = encode_stmt_src(s, counter, output, interner, variants);
3681 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
3682 }
3683 }
3684 }
3685 let bound: HashSet<String> = param_names;
3686 let mut free: Vec<String> = collect_free_vars_expr(expr, interner, &bound).into_iter().collect();
3691 free.sort();
3692 let cap_var = format!("clc_{}", *counter);
3693 *counter += 1;
3694 output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
3695 for fv in &free {
3696 output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
3697 }
3698 output.push_str(&format!(
3699 "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
3700 var, params_var, body_var, cap_var
3701 ));
3702 }
3703 Expr::CallExpr { callee, args } => {
3704 let callee_var = encode_expr_src(callee, counter, output, interner, variants);
3705 let args_var = format!("cea_{}", *counter);
3706 *counter += 1;
3707 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
3708 for a in args {
3709 let av = encode_expr_src(a, counter, output, interner, variants);
3710 output.push_str(&format!("Push {} to {}.\n", av, args_var));
3711 }
3712 output.push_str(&format!(
3713 "Let {} be a new CCallExpr with target {} and args {}.\n",
3714 var, callee_var, args_var
3715 ));
3716 }
3717 Expr::Give { value } => {
3718 let inner_var = encode_expr_src(value, counter, output, interner, variants);
3719 output.push_str(&format!("Let {} be {}.\n", var, inner_var));
3720 }
3721 Expr::Escape { code, .. } => {
3722 let code_str = interner.resolve(*code);
3723 output.push_str(&format!(
3724 "Let {} be a new CEscExpr with code \"{}\".\n",
3725 var, code_str.replace('\"', "\\\"")
3726 ));
3727 }
3728 Expr::List(elems) => {
3729 let items_var = format!("litems_{}", *counter);
3730 *counter += 1;
3731 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
3732 for elem in elems {
3733 let elem_var = encode_expr_src(elem, counter, output, interner, variants);
3734 output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
3735 }
3736 output.push_str(&format!(
3737 "Let {} be a new CList with items {}.\n",
3738 var, items_var
3739 ));
3740 }
3741 Expr::ManifestOf { zone } => {
3742 let zone_var = encode_expr_src(zone, counter, output, interner, variants);
3743 output.push_str(&format!(
3744 "Let {} be a new CManifestOf with zn {}.\n",
3745 var, zone_var
3746 ));
3747 }
3748 Expr::ChunkAt { index, zone } => {
3749 let idx_var = encode_expr_src(index, counter, output, interner, variants);
3750 let zone_var = encode_expr_src(zone, counter, output, interner, variants);
3751 output.push_str(&format!(
3752 "Let {} be a new CChunkAt with idx {} and zn {}.\n",
3753 var, idx_var, zone_var
3754 ));
3755 }
3756 Expr::WithCapacity { value, .. } => {
3757 return encode_expr_src(value, counter, output, interner, variants);
3761 }
3762 _ => {
3763 output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
3764 }
3765 }
3766
3767 var
3768}
3769
3770fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3771 let var = format!("s_{}", *counter);
3772 *counter += 1;
3773
3774 match stmt {
3775 Stmt::Let { var: name, value, .. } => {
3776 let name_str = interner.resolve(*name);
3777 let expr_var = encode_expr_src(value, counter, output, interner, variants);
3778 output.push_str(&format!(
3779 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3780 var, name_str, expr_var
3781 ));
3782 }
3783 Stmt::Set { target, value } => {
3784 let name_str = interner.resolve(*target);
3785 let expr_var = encode_expr_src(value, counter, output, interner, variants);
3786 output.push_str(&format!(
3787 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3788 var, name_str, expr_var
3789 ));
3790 }
3791 Stmt::If { cond, then_block, else_block } => {
3792 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
3793 let then_stmts: Vec<&Stmt> = then_block.iter().collect();
3794 let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
3795 let else_var = if let Some(els) = else_block {
3796 let else_stmts: Vec<&Stmt> = els.iter().collect();
3797 encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
3798 } else {
3799 let empty_var = format!("emptyBlock_{}", *counter);
3800 *counter += 1;
3801 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
3802 empty_var
3803 };
3804 output.push_str(&format!(
3805 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3806 var, cond_var, then_var, else_var
3807 ));
3808 }
3809 Stmt::While { cond, body, .. } => {
3810 let cond_var = encode_expr_src(cond, counter, output, interner, variants);
3811 let body_stmts: Vec<&Stmt> = body.iter().collect();
3812 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
3813 output.push_str(&format!(
3814 "Let {} be a new CWhile with cond {} and body {}.\n",
3815 var, cond_var, body_var
3816 ));
3817 }
3818 Stmt::Splice { body } => {
3819 let cond_var = format!("splice_cond_{}", *counter);
3823 *counter += 1;
3824 output.push_str(&format!("Let {} be a new CBool with value true.\n", cond_var));
3825 let body_stmts: Vec<&Stmt> = body.iter().collect();
3826 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
3827 let empty_var = format!("emptyBlock_{}", *counter);
3828 *counter += 1;
3829 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
3830 output.push_str(&format!(
3831 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3832 var, cond_var, body_var, empty_var
3833 ));
3834 }
3835 Stmt::Return { value } => {
3836 if let Some(expr) = value {
3837 let expr_var = encode_expr_src(expr, counter, output, interner, variants);
3838 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
3839 } else {
3840 let nothing_var = format!("e_{}", *counter);
3841 *counter += 1;
3842 output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
3843 output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
3844 }
3845 }
3846 Stmt::Show { object, .. } => {
3847 let expr_var = encode_expr_src(object, counter, output, interner, variants);
3848 output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
3849 }
3850 Stmt::Call { function, args } => {
3851 let fn_name = interner.resolve(*function);
3852 let args_var = format!("callSArgs_{}", *counter);
3853 *counter += 1;
3854 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
3855 for arg in args {
3856 let arg_var = encode_expr_src(arg, counter, output, interner, variants);
3857 output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
3858 }
3859 output.push_str(&format!(
3860 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
3861 var, fn_name, args_var
3862 ));
3863 }
3864 Stmt::Push { value, collection } => {
3865 let val_var = encode_expr_src(value, counter, output, interner, variants);
3866 let coll_name = extract_ident_name(collection, interner);
3867 output.push_str(&format!(
3868 "Let {} be a new CPush with expr {} and target \"{}\".\n",
3869 var, val_var, coll_name
3870 ));
3871 }
3872 Stmt::SetIndex { collection, index, value } => {
3873 let coll_name = extract_ident_name(collection, interner);
3874 let idx_var = encode_expr_src(index, counter, output, interner, variants);
3875 let val_var = encode_expr_src(value, counter, output, interner, variants);
3876 output.push_str(&format!(
3877 "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
3878 var, coll_name, idx_var, val_var
3879 ));
3880 }
3881 Stmt::SetField { object, field, value } => {
3882 let map_name = extract_ident_name(object, interner);
3883 let field_name = interner.resolve(*field);
3884 let key_var = format!("e_{}", *counter);
3885 *counter += 1;
3886 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
3887 let val_var = encode_expr_src(value, counter, output, interner, variants);
3888 output.push_str(&format!(
3889 "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
3890 var, map_name, key_var, val_var
3891 ));
3892 }
3893 Stmt::Pop { collection, .. } => {
3894 let coll_name = extract_ident_name(collection, interner);
3895 output.push_str(&format!(
3896 "Let {} be a new CPop with target \"{}\".\n",
3897 var, coll_name
3898 ));
3899 }
3900 Stmt::Add { value, collection } => {
3901 let val_var = encode_expr_src(value, counter, output, interner, variants);
3902 let coll_name = extract_ident_name(collection, interner);
3903 output.push_str(&format!(
3904 "Let {} be a new CAdd with elem {} and target \"{}\".\n",
3905 var, val_var, coll_name
3906 ));
3907 }
3908 Stmt::Remove { value, collection } => {
3909 let val_var = encode_expr_src(value, counter, output, interner, variants);
3910 let coll_name = extract_ident_name(collection, interner);
3911 output.push_str(&format!(
3912 "Let {} be a new CRemove with elem {} and target \"{}\".\n",
3913 var, val_var, coll_name
3914 ));
3915 }
3916 Stmt::Inspect { .. } => {
3917 return String::new(); }
3919 Stmt::Repeat { .. } => {
3920 return String::new(); }
3922 Stmt::Break => {
3923 output.push_str(&format!("Let {} be a new CBreak.\n", var));
3924 }
3925 Stmt::RuntimeAssert { condition, hard } => {
3926 let cond_var = encode_expr_src(condition, counter, output, interner, variants);
3927 let msg_var = format!("e_{}", *counter);
3928 *counter += 1;
3929 output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
3930 let variant = if *hard { "CHardAssert" } else { "CRuntimeAssert" };
3934 output.push_str(&format!(
3935 "Let {} be a new {} with cond {} and msg {}.\n",
3936 var, variant, cond_var, msg_var
3937 ));
3938 }
3939 Stmt::Give { object, recipient } => {
3940 let expr_var = encode_expr_src(object, counter, output, interner, variants);
3941 let target_name = extract_ident_name(recipient, interner);
3942 output.push_str(&format!(
3943 "Let {} be a new CGive with expr {} and target \"{}\".\n",
3944 var, expr_var, target_name
3945 ));
3946 }
3947 Stmt::Escape { code, .. } => {
3948 let code_str = interner.resolve(*code);
3949 output.push_str(&format!(
3950 "Let {} be a new CEscStmt with code \"{}\".\n",
3951 var, code_str.replace('\"', "\\\"")
3952 ));
3953 }
3954 Stmt::Sleep { milliseconds } => {
3955 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
3956 output.push_str(&format!(
3957 "Let {} be a new CSleep with duration {}.\n",
3958 var, dur_var
3959 ));
3960 }
3961 Stmt::ReadFrom { var: read_var, source } => {
3962 let var_name = interner.resolve(*read_var);
3963 match source {
3964 ReadSource::Console => {
3965 output.push_str(&format!(
3966 "Let {} be a new CReadConsole with target \"{}\".\n",
3967 var, var_name
3968 ));
3969 }
3970 ReadSource::File(path_expr) => {
3971 let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
3972 output.push_str(&format!(
3973 "Let {} be a new CReadFile with path {} and target \"{}\".\n",
3974 var, path_var, var_name
3975 ));
3976 }
3977 }
3978 }
3979 Stmt::WriteFile { content, path } => {
3980 let path_var = encode_expr_src(path, counter, output, interner, variants);
3981 let content_var = encode_expr_src(content, counter, output, interner, variants);
3982 output.push_str(&format!(
3983 "Let {} be a new CWriteFile with path {} and content {}.\n",
3984 var, path_var, content_var
3985 ));
3986 }
3987 Stmt::Check { source_text, .. } => {
3988 let pred_var = format!("e_{}", *counter);
3989 *counter += 1;
3990 output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
3991 let msg_var = format!("e_{}", *counter);
3992 *counter += 1;
3993 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
3994 output.push_str(&format!(
3995 "Let {} be a new CCheck with predicate {} and msg {}.\n",
3996 var, pred_var, msg_var
3997 ));
3998 }
3999 Stmt::Assert { .. } => {
4000 let prop_var = format!("e_{}", *counter);
4001 *counter += 1;
4002 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
4003 output.push_str(&format!(
4004 "Let {} be a new CAssert with proposition {}.\n",
4005 var, prop_var
4006 ));
4007 }
4008 Stmt::Trust { justification, .. } => {
4009 let prop_var = format!("e_{}", *counter);
4010 *counter += 1;
4011 output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
4012 let just_str = interner.resolve(*justification);
4013 output.push_str(&format!(
4014 "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
4015 var, prop_var, just_str
4016 ));
4017 }
4018 Stmt::Require { crate_name, .. } => {
4019 let dep_name = interner.resolve(*crate_name);
4020 output.push_str(&format!(
4021 "Let {} be a new CRequire with dependency \"{}\".\n",
4022 var, dep_name
4023 ));
4024 }
4025 Stmt::MergeCrdt { source, target } => {
4026 let source_var = encode_expr_src(source, counter, output, interner, variants);
4027 let target_name = extract_ident_name(target, interner);
4030 output.push_str(&format!(
4031 "Let {} be a new CMerge with target \"{}\" and other {}.\n",
4032 var, target_name, source_var
4033 ));
4034 }
4035 Stmt::IncreaseCrdt { object, field, amount } => {
4036 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
4037 let target_name =
4040 format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field));
4041 output.push_str(&format!(
4042 "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
4043 var, target_name, amount_var
4044 ));
4045 }
4046 Stmt::DecreaseCrdt { object, field, amount } => {
4047 let amount_var = encode_expr_src(amount, counter, output, interner, variants);
4048 let target_name =
4049 format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field));
4050 output.push_str(&format!(
4051 "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
4052 var, target_name, amount_var
4053 ));
4054 }
4055 Stmt::AppendToSequence { sequence, value } => {
4056 let value_var = encode_expr_src(value, counter, output, interner, variants);
4057 let target_name = extract_ident_name(sequence, interner);
4058 output.push_str(&format!(
4059 "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
4060 var, target_name, value_var
4061 ));
4062 }
4063 Stmt::ResolveConflict { object, field, .. } => {
4064 let target_name =
4065 format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field));
4066 output.push_str(&format!(
4067 "Let {} be a new CResolve with target \"{}\".\n",
4068 var, target_name
4069 ));
4070 }
4071 Stmt::Sync { var: sync_var, topic } => {
4072 let topic_var = encode_expr_src(topic, counter, output, interner, variants);
4073 let var_name = interner.resolve(*sync_var);
4074 output.push_str(&format!(
4075 "Let {} be a new CSync with target \"{}\" and channel {}.\n",
4076 var, var_name, topic_var
4077 ));
4078 }
4079 Stmt::Mount { var: mount_var, path } => {
4080 let path_var = encode_expr_src(path, counter, output, interner, variants);
4081 let var_name = interner.resolve(*mount_var);
4082 output.push_str(&format!(
4083 "Let {} be a new CMount with target \"{}\" and path {}.\n",
4084 var, var_name, path_var
4085 ));
4086 }
4087 Stmt::Concurrent { tasks } => {
4088 let branches_var = format!("e_{}", *counter);
4089 *counter += 1;
4090 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
4091 for stmt in tasks.iter() {
4094 let branch_var = format!("e_{}", *counter);
4095 *counter += 1;
4096 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
4097 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
4098 if !sv.is_empty() {
4099 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
4100 }
4101 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
4102 }
4103 output.push_str(&format!(
4104 "Let {} be a new CConcurrent with branches {}.\n",
4105 var, branches_var
4106 ));
4107 }
4108 Stmt::Parallel { tasks } => {
4109 let branches_var = format!("e_{}", *counter);
4110 *counter += 1;
4111 output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
4112 for stmt in tasks.iter() {
4114 let branch_var = format!("e_{}", *counter);
4115 *counter += 1;
4116 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
4117 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
4118 if !sv.is_empty() {
4119 output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
4120 }
4121 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
4122 }
4123 output.push_str(&format!(
4124 "Let {} be a new CParallel with branches {}.\n",
4125 var, branches_var
4126 ));
4127 }
4128 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
4129 let func_name = interner.resolve(*function);
4130 let args_var = format!("e_{}", *counter);
4131 *counter += 1;
4132 output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
4133 for arg in args {
4134 let av = encode_expr_src(arg, counter, output, interner, variants);
4135 output.push_str(&format!("Push {} to {}.\n", av, args_var));
4136 }
4137 let body_var = format!("e_{}", *counter);
4138 *counter += 1;
4139 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
4140 let call_var = format!("e_{}", *counter);
4141 *counter += 1;
4142 output.push_str(&format!(
4143 "Let {} be a new CCallS with name \"{}\" and args {}.\n",
4144 call_var, func_name, args_var
4145 ));
4146 output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
4147 let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
4148 interner.resolve(*handle).to_string()
4149 } else {
4150 "_task".to_string()
4151 };
4152 output.push_str(&format!(
4153 "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
4154 var, body_var, handle_name
4155 ));
4156 }
4157 Stmt::StopTask { handle } => {
4158 let handle_var = encode_expr_src(handle, counter, output, interner, variants);
4159 output.push_str(&format!(
4160 "Let {} be a new CStopTask with handle {}.\n",
4161 var, handle_var
4162 ));
4163 }
4164 Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
4165 let cap = capacity.unwrap_or(32);
4166 let cap_var = format!("e_{}", *counter);
4167 *counter += 1;
4168 output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
4169 let pipe_name = interner.resolve(*pipe_var);
4170 output.push_str(&format!(
4171 "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
4172 var, pipe_name, cap_var
4173 ));
4174 }
4175 Stmt::SendPipe { value, pipe } => {
4176 let val_var = encode_expr_src(value, counter, output, interner, variants);
4177 let pipe_name = match pipe {
4178 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4179 _ => "pipe".to_string(),
4180 };
4181 output.push_str(&format!(
4182 "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
4183 var, pipe_name, val_var
4184 ));
4185 }
4186 Stmt::ReceivePipe { var: recv_var, pipe } => {
4187 let recv_name = interner.resolve(*recv_var);
4188 let pipe_name = match pipe {
4189 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4190 _ => "pipe".to_string(),
4191 };
4192 output.push_str(&format!(
4193 "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
4194 var, pipe_name, recv_name
4195 ));
4196 }
4197 Stmt::TrySendPipe { value, pipe, .. } => {
4198 let val_var = encode_expr_src(value, counter, output, interner, variants);
4199 let pipe_name = match pipe {
4200 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4201 _ => "pipe".to_string(),
4202 };
4203 output.push_str(&format!(
4204 "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
4205 var, pipe_name, val_var
4206 ));
4207 }
4208 Stmt::TryReceivePipe { var: recv_var, pipe } => {
4209 let recv_name = interner.resolve(*recv_var);
4210 let pipe_name = match pipe {
4211 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4212 _ => "pipe".to_string(),
4213 };
4214 output.push_str(&format!(
4215 "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
4216 var, pipe_name, recv_name
4217 ));
4218 }
4219 Stmt::Select { branches } => {
4220 let branches_var = format!("e_{}", *counter);
4221 *counter += 1;
4222 output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
4223 for branch in branches {
4224 match branch {
4225 SelectBranch::Receive { var: recv_var, pipe, body } => {
4226 let recv_name = interner.resolve(*recv_var);
4227 let pipe_name = match pipe {
4228 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4229 _ => "pipe".to_string(),
4230 };
4231 let body_var = format!("e_{}", *counter);
4232 *counter += 1;
4233 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
4234 for stmt in body.iter() {
4235 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
4236 if !sv.is_empty() {
4237 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
4238 }
4239 }
4240 let branch_var = format!("e_{}", *counter);
4241 *counter += 1;
4242 output.push_str(&format!(
4243 "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
4244 branch_var, pipe_name, recv_name, body_var
4245 ));
4246 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
4247 }
4248 SelectBranch::Timeout { milliseconds, body } => {
4249 let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
4250 let body_var = format!("e_{}", *counter);
4251 *counter += 1;
4252 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
4253 for stmt in body.iter() {
4254 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
4255 if !sv.is_empty() {
4256 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
4257 }
4258 }
4259 let branch_var = format!("e_{}", *counter);
4260 *counter += 1;
4261 output.push_str(&format!(
4262 "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
4263 branch_var, dur_var, body_var
4264 ));
4265 output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
4266 }
4267 }
4268 }
4269 output.push_str(&format!(
4270 "Let {} be a new CSelect with branches {}.\n",
4271 var, branches_var
4272 ));
4273 }
4274 Stmt::Spawn { agent_type, name } => {
4275 let agent_name = interner.resolve(*agent_type);
4276 let target_name = interner.resolve(*name);
4277 output.push_str(&format!(
4278 "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
4279 var, agent_name, target_name
4280 ));
4281 }
4282 Stmt::SendMessage { message, destination, .. } => {
4283 let target_var = encode_expr_src(destination, counter, output, interner, variants);
4284 let msg_var = encode_expr_src(message, counter, output, interner, variants);
4285 output.push_str(&format!(
4286 "Let {} be a new CSendMessage with target {} and msg {}.\n",
4287 var, target_var, msg_var
4288 ));
4289 }
4290 Stmt::StreamMessage { values, destination } => {
4294 let target_var = encode_expr_src(destination, counter, output, interner, variants);
4295 let vals_var = encode_expr_src(values, counter, output, interner, variants);
4296 output.push_str(&format!(
4297 "Let {} be a new CStreamMessage with target {} and values {}.\n",
4298 var, target_var, vals_var
4299 ));
4300 }
4301 Stmt::AwaitMessage { into, .. } => {
4302 let await_name = interner.resolve(*into);
4303 output.push_str(&format!(
4304 "Let {} be a new CAwaitMessage with target \"{}\".\n",
4305 var, await_name
4306 ));
4307 }
4308 Stmt::Listen { address, secure: _ } => {
4311 let addr_var = encode_expr_src(address, counter, output, interner, variants);
4312 output.push_str(&format!(
4313 "Let {} be a new CListen with addr {} and handler \"default\".\n",
4314 var, addr_var
4315 ));
4316 }
4317 Stmt::ConnectTo { address, secure: _ } => {
4318 let addr_var = encode_expr_src(address, counter, output, interner, variants);
4319 output.push_str(&format!(
4320 "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
4321 var, addr_var
4322 ));
4323 }
4324 Stmt::Zone { name, body, .. } => {
4325 let zone_name = interner.resolve(*name);
4326 let body_var = format!("e_{}", *counter);
4327 *counter += 1;
4328 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
4329 for stmt in body.iter() {
4330 let sv = encode_stmt_src(stmt, counter, output, interner, variants);
4331 if !sv.is_empty() {
4332 output.push_str(&format!("Push {} to {}.\n", sv, body_var));
4333 }
4334 }
4335 output.push_str(&format!(
4336 "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
4337 var, zone_name, body_var
4338 ));
4339 }
4340 Stmt::LetPeerAgent { var: pa_var, address } => {
4341 let addr_var = encode_expr_src(address, counter, output, interner, variants);
4342 let pa_name = interner.resolve(*pa_var);
4343 output.push_str(&format!(
4344 "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
4345 var, addr_var, pa_name
4346 ));
4347 }
4348 Stmt::FunctionDef { .. }
4361 | Stmt::StructDef { .. }
4362 | Stmt::Theorem(_)
4363 | Stmt::Definition(_)
4364 | Stmt::Axiom(_)
4365 | Stmt::Theory(_) => {
4366 return String::new();
4367 }
4368 }
4369
4370 var
4371}
4372
4373fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
4374 match stmt {
4375 Stmt::Inspect { target, arms, .. } => {
4376 let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
4377 let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
4378
4379 for arm in arms {
4380 if arm.variant.is_none() {
4381 otherwise_stmts = arm.body.iter().collect();
4382 } else {
4383 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
4384 variant_arms.push((arm, body_refs));
4385 }
4386 }
4387
4388 if variant_arms.is_empty() {
4389 let mut result = Vec::new();
4390 for s in &otherwise_stmts {
4391 for v in encode_stmts_src(s, counter, output, interner, variants) {
4392 result.push(v);
4393 }
4394 }
4395 return result;
4396 }
4397
4398 let has_otherwise = !otherwise_stmts.is_empty();
4403 let mut result = Vec::new();
4404
4405 let matched_var_name = if has_otherwise {
4407 let name = format!("__inspectMatched_{}", next_inspect_otherwise_idx());
4412 *counter += 1;
4413 let false_expr = format!("e_{}", *counter);
4414 *counter += 1;
4415 output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
4416 let let_stmt = format!("s_{}", *counter);
4417 *counter += 1;
4418 output.push_str(&format!(
4419 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
4420 let_stmt, name, false_expr
4421 ));
4422 result.push(let_stmt);
4423 Some(name)
4424 } else {
4425 None
4426 };
4427
4428 for (arm, body_stmts) in &variant_arms {
4430 let variant_name = interner.resolve(arm.variant.unwrap());
4431
4432 let tag_target = encode_expr_src(target, counter, output, interner, variants);
4434 let tag_key = format!("e_{}", *counter);
4435 *counter += 1;
4436 output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
4437 let tag_get = format!("e_{}", *counter);
4438 *counter += 1;
4439 output.push_str(&format!(
4440 "Let {} be a new CMapGet with target {} and key {}.\n",
4441 tag_get, tag_target, tag_key
4442 ));
4443 let variant_text = format!("e_{}", *counter);
4444 *counter += 1;
4445 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
4446 let cond_var = format!("e_{}", *counter);
4447 *counter += 1;
4448 output.push_str(&format!(
4449 "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
4450 cond_var, tag_get, variant_text
4451 ));
4452
4453 let then_list = format!("stmtList_{}", *counter);
4455 *counter += 1;
4456 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
4457
4458 if let Some(ref mname) = matched_var_name {
4460 let true_expr = format!("e_{}", *counter);
4461 *counter += 1;
4462 output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
4463 let set_stmt = format!("s_{}", *counter);
4464 *counter += 1;
4465 output.push_str(&format!(
4466 "Let {} be a new CSet with name \"{}\" and expr {}.\n",
4467 set_stmt, mname, true_expr
4468 ));
4469 output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
4470 }
4471
4472 for (field_name, binding_name) in &arm.bindings {
4474 let field_str = interner.resolve(*field_name);
4475 let bind_str = interner.resolve(*binding_name);
4476 let bind_target = encode_expr_src(target, counter, output, interner, variants);
4477 let fkey = format!("e_{}", *counter);
4478 *counter += 1;
4479 output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
4480 let fget = format!("e_{}", *counter);
4481 *counter += 1;
4482 output.push_str(&format!(
4483 "Let {} be a new CMapGet with target {} and key {}.\n",
4484 fget, bind_target, fkey
4485 ));
4486 let bind_let = format!("s_{}", *counter);
4487 *counter += 1;
4488 output.push_str(&format!(
4489 "Let {} be a new CLet with name \"{}\" and expr {}.\n",
4490 bind_let, bind_str, fget
4491 ));
4492 output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
4493 }
4494
4495 for body_stmt in body_stmts {
4497 match body_stmt {
4498 Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
4499 let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
4500 for v in vars {
4501 output.push_str(&format!("Push {} to {}.\n", v, then_list));
4502 }
4503 }
4504 _ => {
4505 let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
4506 if !bvar.is_empty() {
4507 output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
4508 }
4509 }
4510 }
4511 }
4512
4513 let empty_else = format!("stmtList_{}", *counter);
4515 *counter += 1;
4516 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
4517
4518 let if_var = format!("s_{}", *counter);
4520 *counter += 1;
4521 output.push_str(&format!(
4522 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
4523 if_var, cond_var, then_list, empty_else
4524 ));
4525
4526 result.push(if_var);
4527 }
4528
4529 if let Some(ref mname) = matched_var_name {
4531 let matched_ref = format!("e_{}", *counter);
4532 *counter += 1;
4533 output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
4534 let not_matched = format!("e_{}", *counter);
4535 *counter += 1;
4536 output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
4537
4538 let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
4539 let empty_else = format!("stmtList_{}", *counter);
4540 *counter += 1;
4541 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
4542
4543 let otherwise_if = format!("s_{}", *counter);
4544 *counter += 1;
4545 output.push_str(&format!(
4546 "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
4547 otherwise_if, not_matched, otherwise_block, empty_else
4548 ));
4549 result.push(otherwise_if);
4550 }
4551
4552 result
4553 }
4554 Stmt::Repeat { pattern, iterable, body, .. } => {
4555 let loop_var_name = match pattern {
4556 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
4557 Pattern::Tuple(syms) => {
4558 if let Some(s) = syms.first() {
4559 interner.resolve(*s).to_string()
4560 } else {
4561 "item".to_string()
4562 }
4563 }
4564 };
4565
4566 if let Expr::Range { start, end } = iterable {
4568 let start_var = encode_expr_src(start, counter, output, interner, variants);
4569 let end_var = encode_expr_src(end, counter, output, interner, variants);
4570 let body_stmts: Vec<&Stmt> = body.iter().collect();
4571 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
4572 let rr = format!("s_{}", *counter);
4573 *counter += 1;
4574 output.push_str(&format!(
4575 "Let {} be a new CRepeatRange with var \"{}\" and start {} and end {} and body {}.\n",
4576 rr, loop_var_name, start_var, end_var, body_var
4577 ));
4578 return vec![rr];
4579 }
4580
4581 let coll_var = encode_expr_src(iterable, counter, output, interner, variants);
4583 let body_stmts: Vec<&Stmt> = body.iter().collect();
4584 let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
4585 let rep = format!("s_{}", *counter);
4586 *counter += 1;
4587 output.push_str(&format!(
4588 "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
4589 rep, loop_var_name, coll_var, body_var
4590 ));
4591 vec![rep]
4592 }
4593 Stmt::Add { collection, .. }
4597 | Stmt::Remove { collection, .. }
4598 | Stmt::AppendToSequence { sequence: collection, .. } => {
4599 let mut result = Vec::new();
4600 if let Some(fd) = emit_force_dynamic(collection, counter, output, interner) {
4601 result.push(fd);
4602 }
4603 let v = encode_stmt_src(stmt, counter, output, interner, variants);
4604 if !v.is_empty() {
4605 result.push(v);
4606 }
4607 result
4608 }
4609 Stmt::IncreaseCrdt { object, .. }
4613 | Stmt::DecreaseCrdt { object, .. }
4614 | Stmt::ResolveConflict { object, .. }
4615 | Stmt::MergeCrdt { target: object, .. } => {
4616 let mut result = Vec::new();
4617 if let Some(fd) = emit_force_dynamic_struct(object, counter, output, interner) {
4618 result.push(fd);
4619 }
4620 let v = encode_stmt_src(stmt, counter, output, interner, variants);
4621 if !v.is_empty() {
4622 result.push(v);
4623 }
4624 result
4625 }
4626 _ => {
4627 let v = encode_stmt_src(stmt, counter, output, interner, variants);
4628 if v.is_empty() {
4629 vec![]
4630 } else {
4631 vec![v]
4632 }
4633 }
4634 }
4635}
4636
4637fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
4638 let list_var = format!("stmtList_{}", *counter);
4639 *counter += 1;
4640 output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
4641
4642 for stmt in stmts {
4643 for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
4644 output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
4645 }
4646 }
4647
4648 list_var
4649}
4650
4651fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
4652 match expr {
4653 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4654 Expr::FieldAccess { object, field } => {
4659 format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field))
4660 }
4661 _ => "unknown".to_string(),
4662 }
4663}
4664
4665fn crdt_base_var(expr: &Expr, interner: &Interner) -> Option<String> {
4671 match expr {
4672 Expr::FieldAccess { object, .. } => Some(crdt_base_var_root(object, interner)),
4673 _ => None,
4674 }
4675}
4676
4677fn crdt_base_var_root(expr: &Expr, interner: &Interner) -> String {
4678 match expr {
4679 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
4680 Expr::FieldAccess { object, .. } => crdt_base_var_root(object, interner),
4681 _ => "unknown".to_string(),
4682 }
4683}
4684
4685fn emit_force_dynamic_named(base: &str, counter: &mut usize, output: &mut String) -> String {
4688 let v = format!("s_{}", *counter);
4689 *counter += 1;
4690 output.push_str(&format!(
4691 "Let {} be a new CForceDynamic with name \"{}\".\n",
4692 v, base
4693 ));
4694 v
4695}
4696
4697fn emit_force_dynamic(
4700 collection: &Expr,
4701 counter: &mut usize,
4702 output: &mut String,
4703 interner: &Interner,
4704) -> Option<String> {
4705 let base = crdt_base_var(collection, interner)?;
4706 Some(emit_force_dynamic_named(&base, counter, output))
4707}
4708
4709fn emit_force_dynamic_struct(
4712 object: &Expr,
4713 counter: &mut usize,
4714 output: &mut String,
4715 interner: &Interner,
4716) -> Option<String> {
4717 match object {
4718 Expr::Identifier(_) | Expr::FieldAccess { .. } => {
4719 Some(emit_force_dynamic_named(&crdt_base_var_root(object, interner), counter, output))
4720 }
4721 _ => None,
4722 }
4723}
4724
4725
4726pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
4739 let full_source = if program.contains("## Main") || program.contains("## To ") {
4740 program.to_string()
4741 } else {
4742 format!("## Main\n{}", program)
4743 };
4744
4745 let mut interner = Interner::new();
4746 let mut lexer = Lexer::new(&full_source, &mut interner);
4747 let tokens = lexer.tokenize();
4748
4749 let type_registry = {
4750 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
4751 let result = discovery.run_full();
4752 result.types
4753 };
4754
4755 let mut world_state = WorldState::new();
4756 let expr_arena = Arena::new();
4757 let term_arena = Arena::new();
4758 let np_arena = Arena::new();
4759 let sym_arena = Arena::new();
4760 let role_arena = Arena::new();
4761 let pp_arena = Arena::new();
4762 let stmt_arena: Arena<Stmt> = Arena::new();
4763 let imperative_expr_arena: Arena<Expr> = Arena::new();
4764 let type_expr_arena: Arena<TypeExpr> = Arena::new();
4765
4766 let ast_ctx = AstContext::with_types(
4767 &expr_arena, &term_arena, &np_arena, &sym_arena,
4768 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
4769 &type_expr_arena,
4770 );
4771
4772 let mut parser = crate::parser::Parser::new(
4773 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
4774 );
4775 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
4776
4777 let optimized = crate::optimize::optimize_for_projection(
4782 stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
4783 &crate::optimization::OptimizationConfig::from_env(),
4784 );
4785
4786 let mut output = String::new();
4787
4788 for stmt in &optimized {
4789 if matches!(stmt, Stmt::FunctionDef { .. }) {
4790 decompile_stmt(stmt, &interner, &mut output, 0);
4791 output.push('\n');
4792 }
4793 }
4794
4795 output.push_str("## Main\n");
4796 for stmt in &optimized {
4797 if !matches!(stmt, Stmt::FunctionDef { .. }) {
4798 decompile_stmt(stmt, &interner, &mut output, 0);
4799 }
4800 }
4801
4802 Ok(prepend_type_definitions(&full_source, output))
4807}
4808
4809fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
4810 let pad = " ".repeat(indent);
4811 match stmt {
4812 Stmt::FunctionDef { name, params, body, return_type, .. } => {
4813 let fn_name = interner.resolve(*name);
4814 let param_strs: Vec<String> = params
4815 .iter()
4816 .map(|(name, ty)| {
4817 let pname = interner.resolve(*name);
4818 format!("{}: {}", pname, decompile_type_expr(ty, interner))
4819 })
4820 .collect();
4821 let ret_str = if let Some(rt) = return_type {
4822 format!(" -> {}", decompile_type_expr(rt, interner))
4823 } else {
4824 String::new()
4825 };
4826 out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
4827 for s in body.iter() {
4828 decompile_stmt(s, interner, out, indent + 1);
4829 }
4830 }
4831 Stmt::Let { var, value, mutable, .. } => {
4832 let name = interner.resolve(*var);
4833 let expr_str = decompile_expr(value, interner);
4834 if *mutable {
4835 out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
4836 } else {
4837 out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
4838 }
4839 }
4840 Stmt::Set { target, value } => {
4841 let name = interner.resolve(*target);
4842 let expr_str = decompile_expr(value, interner);
4843 out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
4844 }
4845 Stmt::Show { object, .. } => {
4846 let expr_str = decompile_expr(object, interner);
4847 out.push_str(&format!("{}Show {}.\n", pad, expr_str));
4848 }
4849 Stmt::Return { value } => {
4850 if let Some(expr) = value {
4851 let expr_str = decompile_expr(expr, interner);
4852 out.push_str(&format!("{}Return {}.\n", pad, expr_str));
4853 } else {
4854 out.push_str(&format!("{}Return.\n", pad));
4855 }
4856 }
4857 Stmt::If { cond, then_block, else_block } => {
4858 let cond_str = decompile_expr(cond, interner);
4859 out.push_str(&format!("{}If {}:\n", pad, cond_str));
4860 for s in then_block.iter() {
4861 decompile_stmt(s, interner, out, indent + 1);
4862 }
4863 if let Some(els) = else_block {
4864 out.push_str(&format!("{}Otherwise:\n", pad));
4865 for s in els.iter() {
4866 decompile_stmt(s, interner, out, indent + 1);
4867 }
4868 }
4869 }
4870 Stmt::While { cond, body, .. } => {
4871 let cond_str = decompile_expr(cond, interner);
4872 out.push_str(&format!("{}While {}:\n", pad, cond_str));
4873 for s in body.iter() {
4874 decompile_stmt(s, interner, out, indent + 1);
4875 }
4876 }
4877 Stmt::Call { function, args } => {
4878 let fn_name = interner.resolve(*function);
4879 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
4880 if arg_strs.is_empty() {
4881 out.push_str(&format!("{}{}().\n", pad, fn_name));
4882 } else {
4883 out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
4884 }
4885 }
4886 Stmt::Push { value, collection } => {
4887 let val_str = decompile_expr(value, interner);
4888 let coll_str = decompile_expr(collection, interner);
4889 out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
4890 }
4891 Stmt::SetIndex { collection, index, value } => {
4892 let coll_str = decompile_expr(collection, interner);
4893 let idx_str = decompile_expr(index, interner);
4894 let val_str = decompile_expr(value, interner);
4895 out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
4896 }
4897 Stmt::SetField { object, field, value } => {
4898 let obj_str = decompile_expr(object, interner);
4899 let field_name = interner.resolve(*field);
4900 let val_str = decompile_expr(value, interner);
4901 out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
4902 }
4903 Stmt::Repeat { pattern, iterable, body, .. } => {
4904 let var_name = match pattern {
4905 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
4906 Pattern::Tuple(syms) => {
4907 syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
4908 }
4909 };
4910 let iter_str = decompile_expr(iterable, interner);
4911 out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
4912 for s in body.iter() {
4913 decompile_stmt(s, interner, out, indent + 1);
4914 }
4915 }
4916 Stmt::Inspect { target, arms, .. } => {
4917 let target_str = decompile_expr(target, interner);
4918 out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
4919 for arm in arms {
4920 if let Some(variant) = arm.variant {
4921 let variant_name = interner.resolve(variant);
4922 let bindings: Vec<String> = arm.bindings.iter()
4923 .map(|(_, b)| interner.resolve(*b).to_string())
4924 .collect();
4925 if bindings.is_empty() {
4926 out.push_str(&format!("{} When {}:\n", pad, variant_name));
4927 } else {
4928 out.push_str(&format!("{} When {}({}):\n", pad, variant_name, bindings.join(", ")));
4929 }
4930 } else {
4931 out.push_str(&format!("{} Otherwise:\n", pad));
4932 }
4933 for s in arm.body.iter() {
4934 decompile_stmt(s, interner, out, indent + 2);
4935 }
4936 }
4937 }
4938 Stmt::Pop { collection, into } => {
4939 let coll_str = decompile_expr(collection, interner);
4940 if let Some(target) = into {
4941 let target_name = interner.resolve(*target);
4942 out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
4943 } else {
4944 out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
4945 }
4946 }
4947 Stmt::Break => {
4948 out.push_str(&format!("{}Break.\n", pad));
4949 }
4950 Stmt::RuntimeAssert { condition, hard } => {
4951 let cond_str = decompile_expr(condition, interner);
4952 let kw = if *hard { "Require that" } else { "Assert that" };
4955 out.push_str(&format!("{}{} {}.\n", pad, kw, cond_str));
4956 }
4957 Stmt::Add { value, collection } => {
4958 let val_str = decompile_expr(value, interner);
4959 let coll_str = decompile_expr(collection, interner);
4960 out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
4961 }
4962 Stmt::Remove { value, collection } => {
4963 let val_str = decompile_expr(value, interner);
4964 let coll_str = decompile_expr(collection, interner);
4965 out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
4966 }
4967 Stmt::Zone { name, body, .. } => {
4968 let zone_name = interner.resolve(*name);
4969 out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
4970 for s in body.iter() {
4971 decompile_stmt(s, interner, out, indent + 1);
4972 }
4973 }
4974 Stmt::ReadFrom { var, .. } => {
4975 let var_name = interner.resolve(*var);
4976 out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
4977 }
4978 Stmt::WriteFile { content, path } => {
4979 let content_str = decompile_expr(content, interner);
4980 let path_str = decompile_expr(path, interner);
4981 out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
4982 }
4983 Stmt::Sleep { milliseconds } => {
4984 let ms = decompile_expr(milliseconds, interner);
4985 out.push_str(&format!("{}Sleep {}.\n", pad, ms));
4986 }
4987 Stmt::IncreaseCrdt { object, field, amount } => {
4991 let obj = decompile_expr(object, interner);
4992 let fld = interner.resolve(*field);
4993 let amt = decompile_expr(amount, interner);
4994 out.push_str(&format!("{}Increase {}'s {} by {}.\n", pad, obj, fld, amt));
4995 }
4996 Stmt::DecreaseCrdt { object, field, amount } => {
4997 let obj = decompile_expr(object, interner);
4998 let fld = interner.resolve(*field);
4999 let amt = decompile_expr(amount, interner);
5000 out.push_str(&format!("{}Decrease {}'s {} by {}.\n", pad, obj, fld, amt));
5001 }
5002 Stmt::MergeCrdt { source, target } => {
5003 let src = decompile_expr(source, interner);
5004 let tgt = decompile_expr(target, interner);
5005 out.push_str(&format!("{}Merge {} into {}.\n", pad, src, tgt));
5006 }
5007 Stmt::AppendToSequence { sequence, value } => {
5008 let seq = decompile_expr(sequence, interner);
5009 let val = decompile_expr(value, interner);
5010 out.push_str(&format!("{}Append {} to {}.\n", pad, val, seq));
5011 }
5012 Stmt::ResolveConflict { object, field, value } => {
5013 let obj = decompile_expr(object, interner);
5014 let fld = interner.resolve(*field);
5015 let val = decompile_expr(value, interner);
5016 out.push_str(&format!("{}Resolve {}'s {} to {}.\n", pad, obj, fld, val));
5017 }
5018 _ => {
5019 }
5022 }
5023}
5024
5025fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
5026 match expr {
5027 Expr::Literal(lit) => match lit {
5028 Literal::Number(n) => n.to_string(),
5029 Literal::Float(f) => format!("{}", f),
5030 Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
5031 Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
5032 Literal::Nothing => "nothing".to_string(),
5033 Literal::Char(c) => format!("'{}'", c),
5034 Literal::Duration(ns) => format!("{}", ns),
5035 Literal::Date(days) => format!("{}", days),
5036 Literal::Moment(ns) => format!("{}", ns),
5037 Literal::Span { months, days } => format!("{} months {} days", months, days),
5038 Literal::Time(ns) => format!("{}", ns),
5039 },
5040 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
5041 Expr::BinaryOp { op, left, right } => {
5042 let l = if matches!(left, Expr::BinaryOp { .. }) {
5043 format!("({})", decompile_expr(left, interner))
5044 } else {
5045 decompile_expr(left, interner)
5046 };
5047 let r = if matches!(right, Expr::BinaryOp { .. }) {
5048 format!("({})", decompile_expr(right, interner))
5049 } else {
5050 decompile_expr(right, interner)
5051 };
5052 if matches!(op, BinaryOpKind::Shl) {
5055 if let Expr::Literal(Literal::Number(k)) = right {
5057 let multiplier = 1i64 << k;
5058 return format!("{} * {}", l, multiplier);
5059 }
5060 }
5061 if matches!(op, BinaryOpKind::Shr) {
5062 if let Expr::Literal(Literal::Number(k)) = right {
5064 let divisor = 1i64 << k;
5065 return format!("{} / {}", l, divisor);
5066 }
5067 }
5068 let op_str = match op {
5069 BinaryOpKind::Add => "+",
5070 BinaryOpKind::Subtract => "-",
5071 BinaryOpKind::Multiply => "*",
5072 BinaryOpKind::Divide | BinaryOpKind::ExactDivide => "/",
5073 BinaryOpKind::FloorDivide => "//",
5074 BinaryOpKind::Modulo => "%",
5075 BinaryOpKind::Eq => "equals",
5076 BinaryOpKind::NotEq => "is not",
5077 BinaryOpKind::Lt => "is less than",
5078 BinaryOpKind::Gt => "is greater than",
5079 BinaryOpKind::LtEq => "is at most",
5080 BinaryOpKind::GtEq => "is at least",
5081 BinaryOpKind::And => "and",
5082 BinaryOpKind::Or => "or",
5083 BinaryOpKind::Concat => "+",
5084 BinaryOpKind::SeqConcat => "followed by",
5085 BinaryOpKind::ApproxEq => "is approximately",
5086 BinaryOpKind::Pow => "**",
5087 BinaryOpKind::BitXor => "+",
5088 BinaryOpKind::BitAnd => "&",
5089 BinaryOpKind::BitOr => "|",
5090 BinaryOpKind::Shl => "*",
5091 BinaryOpKind::Shr => "/",
5092 };
5093 format!("{} {} {}", l, op_str, r)
5094 }
5095 Expr::Not { operand } => {
5096 let inner = decompile_expr(operand, interner);
5100 format!("not ({})", inner)
5101 }
5102 Expr::Call { function, args } => {
5103 let fn_name = interner.resolve(*function);
5104 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
5105 if arg_strs.is_empty() {
5106 format!("{}()", fn_name)
5107 } else {
5108 format!("{}({})", fn_name, arg_strs.join(", "))
5109 }
5110 }
5111 Expr::Index { collection, index } => {
5112 let coll = decompile_expr(collection, interner);
5113 let idx = decompile_expr(index, interner);
5114 format!("item {} of {}", idx, coll)
5115 }
5116 Expr::Length { collection } => {
5117 let coll = decompile_expr(collection, interner);
5118 format!("length of {}", coll)
5119 }
5120 Expr::FieldAccess { object, field } => {
5121 let obj = decompile_expr(object, interner);
5122 let field_name = interner.resolve(*field);
5123 format!("{}'s {}", obj, field_name)
5127 }
5128 Expr::New { type_name, .. } => {
5129 let tn = interner.resolve(*type_name);
5130 format!("a new {}", tn)
5131 }
5132 Expr::NewVariant { variant, fields, .. } => {
5133 let vn = interner.resolve(*variant);
5134 if fields.is_empty() {
5135 format!("a new {}", vn)
5136 } else {
5137 let parts: Vec<String> = fields.iter().map(|(name, val)| {
5138 let n = interner.resolve(*name);
5139 let v = decompile_expr(val, interner);
5140 format!("{} {}", n, v)
5141 }).collect();
5142 format!("a new {} with {}", vn, parts.join(" and "))
5143 }
5144 }
5145 Expr::InterpolatedString(parts) => {
5146 let mut result = String::new();
5147 for part in parts {
5148 match part {
5149 StringPart::Literal(sym) => {
5150 result.push_str(&interner.resolve(*sym));
5151 }
5152 StringPart::Expr { value, debug, .. } => {
5153 let expr_str = decompile_expr(value, interner);
5154 if *debug {
5155 result.push_str(&format!("{{{}=}}", expr_str));
5156 } else {
5157 result.push_str(&format!("{{{}}}", expr_str));
5158 }
5159 }
5160 }
5161 }
5162 format!("\"{}\"", result)
5163 }
5164 Expr::Slice { collection, start, end } => {
5165 let coll = decompile_expr(collection, interner);
5166 let s = decompile_expr(start, interner);
5167 let e = decompile_expr(end, interner);
5168 format!("{} {} through {}", coll, s, e)
5169 }
5170 Expr::Copy { expr } => {
5171 let inner = decompile_expr(expr, interner);
5172 format!("copy of {}", inner)
5173 }
5174 Expr::Give { value } => {
5175 let inner = decompile_expr(value, interner);
5176 format!("Give {}", inner)
5177 }
5178 Expr::Contains { collection, value } => {
5179 let coll = decompile_expr(collection, interner);
5180 let val = decompile_expr(value, interner);
5181 format!("{} contains {}", coll, val)
5182 }
5183 Expr::Union { left, right } => {
5184 let l = decompile_expr(left, interner);
5185 let r = decompile_expr(right, interner);
5186 format!("{} union {}", l, r)
5187 }
5188 Expr::Intersection { left, right } => {
5189 let l = decompile_expr(left, interner);
5190 let r = decompile_expr(right, interner);
5191 format!("{} intersection {}", l, r)
5192 }
5193 Expr::List(elems) => {
5194 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
5195 format!("[{}]", parts.join(", "))
5196 }
5197 Expr::Tuple(elems) => {
5198 let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
5199 format!("({})", parts.join(", "))
5200 }
5201 Expr::Range { start, end } => {
5202 let s = decompile_expr(start, interner);
5203 let e = decompile_expr(end, interner);
5204 format!("{} to {}", s, e)
5205 }
5206 Expr::OptionSome { value } => {
5207 let inner = decompile_expr(value, interner);
5208 format!("some {}", inner)
5209 }
5210 Expr::OptionNone => "none".to_string(),
5211 Expr::WithCapacity { value, capacity } => {
5212 let val = decompile_expr(value, interner);
5213 let cap = decompile_expr(capacity, interner);
5214 format!("{} with capacity {}", val, cap)
5215 }
5216 Expr::Escape { language, code } => {
5217 let lang = interner.resolve(*language);
5218 let src = interner.resolve(*code);
5219 format!("Escape to {}:\n{}", lang, src)
5220 }
5221 Expr::ManifestOf { zone } => {
5222 let z = decompile_expr(zone, interner);
5223 format!("the manifest of {}", z)
5224 }
5225 Expr::ChunkAt { index, zone } => {
5226 let idx = decompile_expr(index, interner);
5227 let z = decompile_expr(zone, interner);
5228 format!("the chunk at {} in {}", idx, z)
5229 }
5230 Expr::Closure { params, body, return_type } => {
5231 let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
5232 let n = interner.resolve(*name);
5233 let t = decompile_type_expr(ty, interner);
5234 format!("{}: {}", n, t)
5235 }).collect();
5236 let ret = if let Some(rt) = return_type {
5237 format!(" -> {}", decompile_type_expr(rt, interner))
5238 } else {
5239 String::new()
5240 };
5241 match body {
5242 ClosureBody::Expression(expr) => {
5243 let e = decompile_expr(expr, interner);
5244 format!("({}){} -> {}", param_strs.join(", "), ret, e)
5245 }
5246 ClosureBody::Block(_) => {
5247 format!("({}){} -> [block]", param_strs.join(", "), ret)
5248 }
5249 }
5250 }
5251 Expr::CallExpr { callee, args } => {
5252 let c = decompile_expr(callee, interner);
5253 let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
5254 format!("{}({})", c, arg_strs.join(", "))
5255 }
5256 }
5257}
5258
5259fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
5260 match ty {
5261 TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
5262 TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
5263 TypeExpr::Generic { base, params } => {
5264 let base_str = interner.resolve(*base);
5265 let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
5266 format!("{} of {}", base_str, param_strs.join(" and "))
5267 }
5268 TypeExpr::Function { inputs, output } => {
5269 let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
5270 let out_str = decompile_type_expr(output, interner);
5271 format!("fn({}) -> {}", in_strs.join(", "), out_str)
5272 }
5273 TypeExpr::Refinement { base, .. } => {
5274 decompile_type_expr(base, interner)
5275 }
5276 TypeExpr::Persistent { inner } => {
5277 format!("Persistent {}", decompile_type_expr(inner, interner))
5278 }
5279 TypeExpr::Mutable { inner } => {
5280 format!("mutable {}", decompile_type_expr(inner, interner))
5281 }
5282 }
5283}
5284
5285pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
5292 let mut interner = Interner::new();
5293 let mut lexer = Lexer::new(source, &mut interner);
5294 let tokens = lexer.tokenize();
5295
5296 let type_registry = {
5297 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
5298 let result = discovery.run_full();
5299 result.types
5300 };
5301
5302 let mut world_state = WorldState::new();
5303 let expr_arena = Arena::new();
5304 let term_arena = Arena::new();
5305 let np_arena = Arena::new();
5306 let sym_arena = Arena::new();
5307 let role_arena = Arena::new();
5308 let pp_arena = Arena::new();
5309 let stmt_arena: Arena<Stmt> = Arena::new();
5310 let imperative_expr_arena: Arena<Expr> = Arena::new();
5311 let type_expr_arena: Arena<TypeExpr> = Arena::new();
5312
5313 let ast_ctx = AstContext::with_types(
5314 &expr_arena, &term_arena, &np_arena, &sym_arena,
5315 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
5316 &type_expr_arena,
5317 );
5318
5319 let mut parser = crate::parser::Parser::new(
5320 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
5321 );
5322 let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
5323
5324 verify_no_overhead_stmts(&stmts, &interner)
5325}
5326
5327const CORE_VARIANT_NAMES: &[&str] = &[
5328 "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
5329 "CCall", "CIndex", "CLen", "CMapGet",
5330 "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
5331 "CCallS", "CPush", "CSetIdx", "CMapSet",
5332 "CFuncDef", "CProg",
5333 "CManifestOf", "CChunkAt",
5334 "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
5335];
5336
5337const CORE_TYPE_NAMES: &[&str] = &[
5342 "CVal", "CExpr", "CStmt", "CFunc", "CFuncDef", "CProgram", "CProg",
5343 "CoreOut", "CoreOutR", "PEState", "PEStateR", "PEMiniState", "PEMiniR",
5344];
5345
5346fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
5347 for stmt in stmts {
5348 check_stmt_overhead(stmt, interner)?;
5349 }
5350 Ok(())
5351}
5352
5353fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
5354 match stmt {
5355 Stmt::Inspect { arms, .. } => {
5356 for arm in arms {
5357 if let Some(variant) = arm.variant {
5358 let variant_name = interner.resolve(variant);
5359 if CORE_VARIANT_NAMES.contains(&variant_name) {
5360 return Err(format!(
5361 "Interpretive overhead: Inspect dispatches on Core variant '{}'",
5362 variant_name
5363 ));
5364 }
5365 }
5366 for s in arm.body.iter() {
5367 check_stmt_overhead(s, interner)?;
5368 }
5369 }
5370 }
5371 Stmt::If { cond, then_block, else_block } => {
5372 check_expr_overhead(cond, interner)?;
5373 for s in then_block.iter() {
5374 check_stmt_overhead(s, interner)?;
5375 }
5376 if let Some(els) = else_block {
5377 for s in els.iter() {
5378 check_stmt_overhead(s, interner)?;
5379 }
5380 }
5381 }
5382 Stmt::While { cond, body, .. } => {
5383 check_expr_overhead(cond, interner)?;
5384 for s in body.iter() {
5385 check_stmt_overhead(s, interner)?;
5386 }
5387 }
5388 Stmt::FunctionDef { body, .. } => {
5389 for s in body.iter() {
5390 check_stmt_overhead(s, interner)?;
5391 }
5392 }
5393 Stmt::Repeat { body, .. } => {
5394 for s in body.iter() {
5395 check_stmt_overhead(s, interner)?;
5396 }
5397 }
5398 Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
5399 check_expr_overhead(value, interner)?;
5400 }
5401 Stmt::Return { value } => {
5402 if let Some(v) = value {
5403 check_expr_overhead(v, interner)?;
5404 }
5405 }
5406 _ => {}
5407 }
5408 Ok(())
5409}
5410
5411fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
5412 match expr {
5413 Expr::Index { collection, index } => {
5414 if let Expr::Identifier(coll_sym) = collection {
5416 let coll_name = interner.resolve(*coll_sym);
5417 if coll_name == "env" {
5418 if let Expr::Literal(Literal::Text(_)) = index {
5419 return Err(
5420 "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
5421 );
5422 }
5423 }
5424 }
5425 check_expr_overhead(collection, interner)?;
5426 check_expr_overhead(index, interner)?;
5427 }
5428 Expr::New { type_name, .. } => {
5429 let tn = interner.resolve(*type_name);
5430 if CORE_VARIANT_NAMES.contains(&tn) {
5431 return Err(format!(
5432 "Interpretive overhead: Core type constructor 'new {}'", tn
5433 ));
5434 }
5435 }
5436 Expr::NewVariant { variant, .. } => {
5437 let vn = interner.resolve(*variant);
5438 if CORE_VARIANT_NAMES.contains(&vn) {
5439 return Err(format!(
5440 "Interpretive overhead: Core variant constructor '{}'", vn
5441 ));
5442 }
5443 }
5444 Expr::Call { function, args } => {
5445 let fn_name = interner.resolve(*function);
5446 if CORE_VARIANT_NAMES.contains(&fn_name) {
5447 return Err(format!(
5448 "Interpretive overhead: Core variant call '{}'", fn_name
5449 ));
5450 }
5451 for a in args {
5452 check_expr_overhead(a, interner)?;
5453 }
5454 }
5455 Expr::BinaryOp { left, right, .. } => {
5456 check_expr_overhead(left, interner)?;
5457 check_expr_overhead(right, interner)?;
5458 }
5459 Expr::Not { operand } => {
5460 check_expr_overhead(operand, interner)?;
5461 }
5462 Expr::Length { collection } => {
5463 check_expr_overhead(collection, interner)?;
5464 }
5465 Expr::FieldAccess { object, .. } => {
5466 check_expr_overhead(object, interner)?;
5467 }
5468 _ => {}
5469 }
5470 Ok(())
5471}
5472
5473const DISPATCH_FN_NAMES: &[&str] = &[
5477 "peExpr", "peBlock", "coreEval", "coreExecBlock", "applyBinOp",
5478];
5479
5480pub fn count_dispatch(source: &str) -> usize {
5493 let mut interner = Interner::new();
5494 let mut lexer = Lexer::new(source, &mut interner);
5495 let tokens = lexer.tokenize();
5496
5497 let type_registry = {
5498 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
5499 let result = discovery.run_full();
5500 result.types
5501 };
5502
5503 let mut world_state = WorldState::new();
5504 let expr_arena = Arena::new();
5505 let term_arena = Arena::new();
5506 let np_arena = Arena::new();
5507 let sym_arena = Arena::new();
5508 let role_arena = Arena::new();
5509 let pp_arena = Arena::new();
5510 let stmt_arena: Arena<Stmt> = Arena::new();
5511 let imperative_expr_arena: Arena<Expr> = Arena::new();
5512 let type_expr_arena: Arena<TypeExpr> = Arena::new();
5513
5514 let ast_ctx = AstContext::with_types(
5515 &expr_arena, &term_arena, &np_arena, &sym_arena,
5516 &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
5517 &type_expr_arena,
5518 );
5519
5520 let mut parser = crate::parser::Parser::new(
5521 tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
5522 );
5523 let stmts = match parser.parse_program() {
5524 Ok(s) => s,
5525 Err(_) => return usize::MAX,
5528 };
5529
5530 let mut count = 0usize;
5531 for stmt in &stmts {
5532 count_stmt_dispatch(stmt, &interner, &mut count);
5533 }
5534 count
5535}
5536
5537fn count_block_dispatch(block: &[Stmt], interner: &Interner, count: &mut usize) {
5538 for s in block {
5539 count_stmt_dispatch(s, interner, count);
5540 }
5541}
5542
5543fn type_expr_mentions_core(t: &TypeExpr, interner: &Interner) -> bool {
5546 let is_core = |s| {
5547 let n = interner.resolve(s);
5548 CORE_TYPE_NAMES.contains(&n) || CORE_VARIANT_NAMES.contains(&n)
5549 };
5550 match t {
5551 TypeExpr::Primitive(s) | TypeExpr::Named(s) => is_core(*s),
5552 TypeExpr::Generic { base, params } => {
5553 is_core(*base) || params.iter().any(|p| type_expr_mentions_core(p, interner))
5554 }
5555 TypeExpr::Function { inputs, output } => {
5556 inputs.iter().any(|p| type_expr_mentions_core(p, interner))
5557 || type_expr_mentions_core(output, interner)
5558 }
5559 TypeExpr::Refinement { base, .. } => type_expr_mentions_core(base, interner),
5560 TypeExpr::Persistent { inner } | TypeExpr::Mutable { inner } => {
5561 type_expr_mentions_core(inner, interner)
5562 }
5563 }
5564}
5565
5566fn count_escape_code_dispatch(text: &str, count: &mut usize) {
5570 for word in text.split(|c: char| !c.is_alphanumeric() && c != '_') {
5571 if !word.is_empty()
5572 && (CORE_VARIANT_NAMES.contains(&word) || DISPATCH_FN_NAMES.contains(&word))
5573 {
5574 *count += 1;
5575 }
5576 }
5577}
5578
5579fn count_stmt_dispatch(stmt: &Stmt, interner: &Interner, count: &mut usize) {
5583 match stmt {
5584 Stmt::Splice { body } => {
5585 for inner in body.iter() {
5586 count_stmt_dispatch(inner, interner, count);
5587 }
5588 }
5589 Stmt::Let { value, ty, .. } => {
5590 if let Some(t) = ty {
5591 if type_expr_mentions_core(t, interner) {
5592 *count += 1;
5593 }
5594 }
5595 count_expr_dispatch(value, interner, count);
5596 }
5597 Stmt::Set { value, .. } => count_expr_dispatch(value, interner, count),
5598 Stmt::Call { function, args } => {
5599 let fn_name = interner.resolve(*function);
5600 if CORE_VARIANT_NAMES.contains(&fn_name) || DISPATCH_FN_NAMES.contains(&fn_name) {
5601 *count += 1;
5602 }
5603 for a in args {
5604 count_expr_dispatch(a, interner, count);
5605 }
5606 }
5607 Stmt::If { cond, then_block, else_block } => {
5608 count_expr_dispatch(cond, interner, count);
5609 count_block_dispatch(then_block, interner, count);
5610 if let Some(els) = else_block {
5611 count_block_dispatch(els, interner, count);
5612 }
5613 }
5614 Stmt::While { cond, body, decreasing } => {
5615 count_expr_dispatch(cond, interner, count);
5616 count_block_dispatch(body, interner, count);
5617 if let Some(d) = decreasing {
5618 count_expr_dispatch(d, interner, count);
5619 }
5620 }
5621 Stmt::Repeat { iterable, body, .. } => {
5622 count_expr_dispatch(iterable, interner, count);
5623 count_block_dispatch(body, interner, count);
5624 }
5625 Stmt::Return { value } => {
5626 if let Some(v) = value {
5627 count_expr_dispatch(v, interner, count);
5628 }
5629 }
5630 Stmt::RuntimeAssert { condition, .. } => count_expr_dispatch(condition, interner, count),
5631 Stmt::Give { object, recipient } | Stmt::Show { object, recipient } => {
5632 count_expr_dispatch(object, interner, count);
5633 count_expr_dispatch(recipient, interner, count);
5634 }
5635 Stmt::SetField { object, value, .. } => {
5636 count_expr_dispatch(object, interner, count);
5637 count_expr_dispatch(value, interner, count);
5638 }
5639 Stmt::FunctionDef { body, .. } => count_block_dispatch(body, interner, count),
5640 Stmt::Inspect { target, arms, .. } => {
5641 count_expr_dispatch(target, interner, count);
5642 for arm in arms {
5643 if let Some(variant) = arm.variant {
5644 if CORE_VARIANT_NAMES.contains(&interner.resolve(variant)) {
5645 *count += 1;
5646 }
5647 }
5648 count_block_dispatch(arm.body, interner, count);
5649 }
5650 }
5651 Stmt::Push { value, collection }
5652 | Stmt::Add { value, collection }
5653 | Stmt::Remove { value, collection } => {
5654 count_expr_dispatch(value, interner, count);
5655 count_expr_dispatch(collection, interner, count);
5656 }
5657 Stmt::Pop { collection, .. } => count_expr_dispatch(collection, interner, count),
5658 Stmt::SetIndex { collection, index, value } => {
5659 count_expr_dispatch(collection, interner, count);
5660 count_expr_dispatch(index, interner, count);
5661 count_expr_dispatch(value, interner, count);
5662 }
5663 Stmt::Zone { body, .. } => count_block_dispatch(body, interner, count),
5664 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
5665 count_block_dispatch(tasks, interner, count)
5666 }
5667 Stmt::ReadFrom { source, .. } => {
5668 if let ReadSource::File(path) = source {
5669 count_expr_dispatch(path, interner, count);
5670 }
5671 }
5672 Stmt::WriteFile { content, path } => {
5673 count_expr_dispatch(content, interner, count);
5674 count_expr_dispatch(path, interner, count);
5675 }
5676 Stmt::SendMessage { message, destination, .. } => {
5677 count_expr_dispatch(message, interner, count);
5678 count_expr_dispatch(destination, interner, count);
5679 }
5680 Stmt::AwaitMessage { source, .. } => count_expr_dispatch(source, interner, count),
5681 Stmt::StreamMessage { values, destination } => {
5682 count_expr_dispatch(values, interner, count);
5683 count_expr_dispatch(destination, interner, count);
5684 }
5685 Stmt::MergeCrdt { source, target } => {
5686 count_expr_dispatch(source, interner, count);
5687 count_expr_dispatch(target, interner, count);
5688 }
5689 Stmt::IncreaseCrdt { object, amount, .. } | Stmt::DecreaseCrdt { object, amount, .. } => {
5690 count_expr_dispatch(object, interner, count);
5691 count_expr_dispatch(amount, interner, count);
5692 }
5693 Stmt::AppendToSequence { sequence, value } => {
5694 count_expr_dispatch(sequence, interner, count);
5695 count_expr_dispatch(value, interner, count);
5696 }
5697 Stmt::ResolveConflict { object, value, .. } => {
5698 count_expr_dispatch(object, interner, count);
5699 count_expr_dispatch(value, interner, count);
5700 }
5701 Stmt::Listen { address, .. } | Stmt::ConnectTo { address, .. } | Stmt::LetPeerAgent { address, .. } => {
5702 count_expr_dispatch(address, interner, count)
5703 }
5704 Stmt::Sleep { milliseconds } => count_expr_dispatch(milliseconds, interner, count),
5705 Stmt::Sync { topic, .. } => count_expr_dispatch(topic, interner, count),
5706 Stmt::Mount { path, .. } => count_expr_dispatch(path, interner, count),
5707 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
5708 let fn_name = interner.resolve(*function);
5709 if CORE_VARIANT_NAMES.contains(&fn_name) || DISPATCH_FN_NAMES.contains(&fn_name) {
5710 *count += 1;
5711 }
5712 for a in args {
5713 count_expr_dispatch(a, interner, count);
5714 }
5715 }
5716 Stmt::SendPipe { value, pipe } | Stmt::TrySendPipe { value, pipe, .. } => {
5717 count_expr_dispatch(value, interner, count);
5718 count_expr_dispatch(pipe, interner, count);
5719 }
5720 Stmt::ReceivePipe { pipe, .. } | Stmt::TryReceivePipe { pipe, .. } => {
5721 count_expr_dispatch(pipe, interner, count)
5722 }
5723 Stmt::StopTask { handle } => count_expr_dispatch(handle, interner, count),
5724 Stmt::Select { branches } => {
5725 for b in branches {
5726 match b {
5727 SelectBranch::Receive { pipe, body, .. } => {
5728 count_expr_dispatch(pipe, interner, count);
5729 count_block_dispatch(body, interner, count);
5730 }
5731 SelectBranch::Timeout { milliseconds, body } => {
5732 count_expr_dispatch(milliseconds, interner, count);
5733 count_block_dispatch(body, interner, count);
5734 }
5735 }
5736 }
5737 }
5738 Stmt::Escape { code, .. } => count_escape_code_dispatch(interner.resolve(*code), count),
5739 Stmt::Break
5742 | Stmt::Assert { .. }
5743 | Stmt::Trust { .. }
5744 | Stmt::StructDef { .. }
5745 | Stmt::Spawn { .. }
5746 | Stmt::Check { .. }
5747 | Stmt::CreatePipe { .. }
5748 | Stmt::Theorem(_)
5749 | Stmt::Definition(_)
5750 | Stmt::Axiom(_)
5751 | Stmt::Theory(_)
5752 | Stmt::Require { .. } => {}
5753 }
5754}
5755
5756fn count_expr_dispatch(expr: &Expr, interner: &Interner, count: &mut usize) {
5760 match expr {
5761 Expr::Literal(_) | Expr::Identifier(_) | Expr::OptionNone => {}
5762 Expr::BinaryOp { left, right, .. }
5763 | Expr::Union { left, right }
5764 | Expr::Intersection { left, right } => {
5765 count_expr_dispatch(left, interner, count);
5766 count_expr_dispatch(right, interner, count);
5767 }
5768 Expr::Not { operand } => count_expr_dispatch(operand, interner, count),
5769 Expr::Call { function, args } => {
5770 let fn_name = interner.resolve(*function);
5771 if CORE_VARIANT_NAMES.contains(&fn_name) || DISPATCH_FN_NAMES.contains(&fn_name) {
5772 *count += 1;
5773 }
5774 for a in args {
5775 count_expr_dispatch(a, interner, count);
5776 }
5777 }
5778 Expr::Index { collection, index } => {
5779 if let Expr::Identifier(coll_sym) = collection {
5782 let coll_name = interner.resolve(*coll_sym);
5783 if coll_name == "env" || coll_name == "funcs" {
5784 *count += 1;
5785 }
5786 }
5787 count_expr_dispatch(collection, interner, count);
5788 count_expr_dispatch(index, interner, count);
5789 }
5790 Expr::Slice { collection, start, end } => {
5791 count_expr_dispatch(collection, interner, count);
5792 count_expr_dispatch(start, interner, count);
5793 count_expr_dispatch(end, interner, count);
5794 }
5795 Expr::Copy { expr: inner } => count_expr_dispatch(inner, interner, count),
5796 Expr::Give { value } => count_expr_dispatch(value, interner, count),
5797 Expr::Length { collection } => count_expr_dispatch(collection, interner, count),
5798 Expr::Contains { collection, value } => {
5799 count_expr_dispatch(collection, interner, count);
5800 count_expr_dispatch(value, interner, count);
5801 }
5802 Expr::ManifestOf { zone } => count_expr_dispatch(zone, interner, count),
5803 Expr::ChunkAt { index, zone } => {
5804 count_expr_dispatch(index, interner, count);
5805 count_expr_dispatch(zone, interner, count);
5806 }
5807 Expr::List(items) | Expr::Tuple(items) => {
5808 for e in items {
5809 count_expr_dispatch(e, interner, count);
5810 }
5811 }
5812 Expr::Range { start, end } => {
5813 count_expr_dispatch(start, interner, count);
5814 count_expr_dispatch(end, interner, count);
5815 }
5816 Expr::FieldAccess { object, .. } => count_expr_dispatch(object, interner, count),
5817 Expr::New { type_name, type_args, init_fields } => {
5818 let tn = interner.resolve(*type_name);
5819 if CORE_VARIANT_NAMES.contains(&tn) || CORE_TYPE_NAMES.contains(&tn) {
5820 *count += 1;
5821 }
5822 for ta in type_args {
5825 if type_expr_mentions_core(ta, interner) {
5826 *count += 1;
5827 }
5828 }
5829 for (_, v) in init_fields {
5830 count_expr_dispatch(v, interner, count);
5831 }
5832 }
5833 Expr::NewVariant { variant, fields, .. } => {
5834 if CORE_VARIANT_NAMES.contains(&interner.resolve(*variant)) {
5835 *count += 1;
5836 }
5837 for (_, v) in fields {
5838 count_expr_dispatch(v, interner, count);
5839 }
5840 }
5841 Expr::Escape { code, .. } => count_escape_code_dispatch(interner.resolve(*code), count),
5842 Expr::OptionSome { value } => count_expr_dispatch(value, interner, count),
5843 Expr::WithCapacity { value, capacity } => {
5844 count_expr_dispatch(value, interner, count);
5845 count_expr_dispatch(capacity, interner, count);
5846 }
5847 Expr::Closure { body, .. } => match body {
5848 ClosureBody::Expression(e) => count_expr_dispatch(e, interner, count),
5849 ClosureBody::Block(b) => count_block_dispatch(b, interner, count),
5850 },
5851 Expr::CallExpr { callee, args } => {
5852 count_expr_dispatch(callee, interner, count);
5853 for a in args {
5854 count_expr_dispatch(a, interner, count);
5855 }
5856 }
5857 Expr::InterpolatedString(parts) => {
5858 for p in parts {
5859 if let StringPart::Expr { value, .. } = p {
5860 count_expr_dispatch(value, interner, count);
5861 }
5862 }
5863 }
5864 }
5865}
5866
5867pub fn pe_source_text() -> &'static str {
5873 include_str!("optimize/pe_source.logos")
5874}
5875
5876pub fn decompile_source_text() -> &'static str {
5877 include_str!("optimize/decompile_source.logos")
5878}
5879
5880pub fn pe_bti_source_text() -> &'static str {
5881 include_str!("optimize/pe_bti_source.logos")
5882}
5883
5884pub fn pe_mini_source_text() -> &'static str {
5885 include_str!("optimize/pe_mini_source.logos")
5886}
5887
5888pub fn core_types_for_pe_source() -> &'static str {
5895 CORE_TYPES_FOR_PE
5896}
5897
5898const CORE_TYPES_FOR_PE: &str = r#"
5899## A CExpr is one of:
5900 A CInt with value Int.
5901 A CFloat with value Real.
5902 A CBool with value Bool.
5903 A CText with value Text.
5904 A CVar with name Text.
5905 A CBinOp with op Text and left CExpr and right CExpr.
5906 A CNot with inner CExpr.
5907 A CCall with name Text and args Seq of CExpr.
5908 A CIndex with coll CExpr and idx CExpr.
5909 A CLen with target CExpr.
5910 A CMapGet with target CExpr and key CExpr.
5911 A CNewSeq.
5912 A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
5913 A CList with items Seq of CExpr.
5914 A CRange with start CExpr and end CExpr.
5915 A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
5916 A CCopy with target CExpr.
5917 A CNewSet.
5918 A CContains with coll CExpr and elem CExpr.
5919 A CUnion with left CExpr and right CExpr.
5920 A CIntersection with left CExpr and right CExpr.
5921 A COptionSome with inner CExpr.
5922 A COptionNone.
5923 A CTuple with items Seq of CExpr.
5924 A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
5925 A CFieldAccess with target CExpr and field Text.
5926 A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
5927 A CCallExpr with target CExpr and args Seq of CExpr.
5928 A CInterpolatedString with parts Seq of CStringPart.
5929 A CDuration with amount CExpr and unit Text.
5930 A CTimeNow.
5931 A CDateToday.
5932 A CEscExpr with code Text.
5933 A CManifestOf with zn CExpr.
5934 A CChunkAt with idx CExpr and zn CExpr.
5935
5936## A CStringPart is one of:
5937 A CLiteralPart with value Text.
5938 A CExprPart with expr CExpr.
5939
5940## A CStmt is one of:
5941 A CLet with name Text and expr CExpr.
5942 A CSet with name Text and expr CExpr.
5943 A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
5944 A CWhile with cond CExpr and body Seq of CStmt.
5945 A CReturn with expr CExpr.
5946 A CShow with expr CExpr.
5947 A CCallS with name Text and args Seq of CExpr.
5948 A CPush with expr CExpr and target Text.
5949 A CSetIdx with target Text and idx CExpr and val CExpr.
5950 A CMapSet with target Text and key CExpr and val CExpr.
5951 A CPop with target Text.
5952 A CRepeat with var Text and coll CExpr and body Seq of CStmt.
5953 A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
5954 A CBreak.
5955 A CAdd with elem CExpr and target Text.
5956 A CRemove with elem CExpr and target Text.
5957 A CForceDynamic with name Text.
5958 A CSetField with target Text and field Text and val CExpr.
5959 A CStructDef with name Text and fieldNames Seq of Text.
5960 A CInspect with target CExpr and arms Seq of CMatchArm.
5961 A CEnumDef with name Text and variants Seq of Text.
5962 A CRuntimeAssert with cond CExpr and msg CExpr.
5963 A CHardAssert with cond CExpr and msg CExpr.
5964 A CGive with expr CExpr and target Text.
5965 A CEscStmt with code Text.
5966 A CSleep with duration CExpr.
5967 A CReadConsole with target Text.
5968 A CReadFile with path CExpr and target Text.
5969 A CWriteFile with path CExpr and content CExpr.
5970 A CCheck with predicate CExpr and msg CExpr.
5971 A CAssert with proposition CExpr.
5972 A CTrust with proposition CExpr and justification Text.
5973 A CRequire with dependency Text.
5974 A CMerge with target Text and other CExpr.
5975 A CIncrease with target Text and amount CExpr.
5976 A CDecrease with target Text and amount CExpr.
5977 A CAppendToSeq with target Text and value CExpr.
5978 A CResolve with target Text.
5979 A CSync with target Text and channel CExpr.
5980 A CMount with target Text and path CExpr.
5981 A CConcurrent with branches Seq of Seq of CStmt.
5982 A CParallel with branches Seq of Seq of CStmt.
5983 A CLaunchTask with body Seq of CStmt and handle Text.
5984 A CStopTask with handle CExpr.
5985 A CSelect with branches Seq of CSelectBranch.
5986 A CCreatePipe with name Text and capacity CExpr.
5987 A CSendPipe with chan Text and value CExpr.
5988 A CReceivePipe with chan Text and target Text.
5989 A CTrySendPipe with chan Text and value CExpr.
5990 A CTryReceivePipe with chan Text and target Text.
5991 A CSpawn with agentType Text and target Text.
5992 A CSendMessage with target CExpr and msg CExpr.
5993 A CStreamMessage with target CExpr and values CExpr.
5994 A CAwaitMessage with target Text.
5995 A CListen with addr CExpr and handler Text.
5996 A CConnectTo with addr CExpr and target Text.
5997 A CZone with name Text and kind Text and body Seq of CStmt.
5998
5999## A CSelectBranch is one of:
6000 A CSelectRecv with chan Text and var Text and body Seq of CStmt.
6001 A CSelectTimeout with duration CExpr and body Seq of CStmt.
6002
6003## A CMatchArm is one of:
6004 A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
6005 A COtherwise with body Seq of CStmt.
6006
6007## A CFunc is one of:
6008 A CFuncDef with name Text and params Seq of Text and paramTypes Seq of Text and returnType Text and body Seq of CStmt.
6009
6010## A CProgram is one of:
6011 A CProg with funcs Seq of CFunc and main Seq of CStmt.
6012
6013## A PEState is one of:
6014 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.
6015
6016## A CVal is one of:
6017 A VInt with value Int.
6018 A VFloat with value Real.
6019 A VBool with value Bool.
6020 A VText with value Text.
6021 A VSeq with items Seq of CVal.
6022 A VMap with entries Map of Text to CVal.
6023 A VError with msg Text.
6024 A VNothing.
6025 A VSet with items Seq of CVal.
6026 A VOption with inner CVal and present Bool.
6027 A VTuple with items Seq of CVal.
6028 A VStruct with typeName Text and fields Map of Text to CVal.
6029 A VVariant with typeName Text and variantName Text and fields Seq of CVal.
6030 A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
6031 A VDuration with millis Int.
6032 A VDate with year Int and month Int and day Int.
6033 A VMoment with millis Int.
6034 A VSpan with startMillis Int and endMillis Int.
6035 A VTime with hour Int and minute Int and second Int.
6036 A VCrdt with kind Text and state Map of Text to CVal.
6037"#;
6038
6039pub fn quote_pe_source() -> Result<String, String> {
6047 let pe_source = pe_source_text();
6048 let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
6049 let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
6050 Ok(format!("{}\n{}", pe_source, encoded))
6051}
6052
6053pub fn projection2_source() -> Result<String, String> {
6072 let pe_source = pe_source_text();
6073
6074 let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
6075
6076 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
6077}
6078
6079pub fn projection3_source() -> Result<String, String> {
6098 let pe_source = pe_source_text();
6099
6100 let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
6101
6102 Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
6103}
6104
6105pub fn run_logos_source(source: &str) -> Result<String, String> {
6110 let compile_output = compile_program_full(source)
6111 .map_err(|e| format!("Compilation failed: {:?}", e))?;
6112
6113 let temp_base = std::env::temp_dir().join("logos_run_source");
6115 std::fs::create_dir_all(&temp_base)
6116 .map_err(|e| format!("mkdir failed: {}", e))?;
6117
6118 let pkg_name = format!(
6119 "logos_run_{}_{}",
6120 std::process::id(),
6121 std::time::SystemTime::now()
6122 .duration_since(std::time::UNIX_EPOCH)
6123 .unwrap()
6124 .as_nanos()
6125 );
6126 let project_dir = temp_base.join(&pkg_name);
6127
6128 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
6130 let workspace_root = manifest_dir.parent().unwrap().parent().unwrap();
6131
6132 let cargo_toml = format!(
6133 r#"[package]
6134name = "{}"
6135version = "0.1.0"
6136edition = "2021"
6137
6138[dependencies]
6139logicaffeine-data = {{ path = "{}/crates/logicaffeine_data" }}
6140logicaffeine-system = {{ path = "{}/crates/logicaffeine_system", features = ["full"] }}
6141tokio = {{ version = "1", features = ["rt-multi-thread", "macros"] }}
6142serde = {{ version = "1", features = ["derive"] }}
6143rayon = "1"
6144"#,
6145 pkg_name,
6146 workspace_root.display(),
6147 workspace_root.display(),
6148 );
6149
6150 std::fs::create_dir_all(project_dir.join("src"))
6151 .map_err(|e| format!("mkdir failed: {}", e))?;
6152 std::fs::write(project_dir.join("Cargo.toml"), cargo_toml)
6153 .map_err(|e| format!("Write Cargo.toml failed: {}", e))?;
6154 std::fs::write(project_dir.join("src/main.rs"), &compile_output.rust_code)
6155 .map_err(|e| format!("Write main.rs failed: {}", e))?;
6156 std::fs::copy(
6160 workspace_root.join("Cargo.lock"),
6161 project_dir.join("Cargo.lock"),
6162 )
6163 .map_err(|e| format!("Seed Cargo.lock failed: {}", e))?;
6164
6165 let shard = std::env::var("NEXTEST_TEST_GROUP_SLOT")
6169 .ok()
6170 .and_then(|s| s.parse::<u64>().ok())
6171 .or_else(|| {
6172 std::env::var("NEXTEST_TEST_GLOBAL_SLOT")
6173 .ok()
6174 .and_then(|s| s.parse::<u64>().ok())
6175 .map(|n| n % 12)
6176 });
6177 let target_dir = match shard {
6178 Some(s) => std::env::temp_dir().join(format!("logos_e2e_cache_{s}")),
6179 None => std::env::temp_dir().join("logos_e2e_cache"),
6180 };
6181 std::fs::create_dir_all(&target_dir)
6182 .map_err(|e| format!("mkdir target failed: {}", e))?;
6183
6184 let mut run_cmd = std::process::Command::new("cargo");
6185 run_cmd
6186 .args(["run", "--quiet"])
6187 .current_dir(&project_dir)
6188 .env("CARGO_TARGET_DIR", &target_dir)
6189 .env("RUST_MIN_STACK", "268435456");
6190 if crate::semantics::collections::reference_scope_active() {
6195 run_cmd.env("LOGOS_VALUE_SEMANTICS", "0");
6196 }
6197 let output = run_cmd
6198 .output()
6199 .map_err(|e| format!("cargo run failed: {}", e))?;
6200
6201 let _ = std::fs::remove_dir_all(&project_dir);
6206 {
6207 let stem = pkg_name.replace('-', "_");
6208 let debug = target_dir.join("debug");
6209 let _ = std::fs::remove_file(debug.join(&pkg_name));
6210 let _ = std::fs::remove_file(debug.join(format!("{pkg_name}.d")));
6211 for sub in ["deps", "incremental", ".fingerprint", "build"] {
6212 if let Ok(rd) = std::fs::read_dir(debug.join(sub)) {
6213 for e in rd.flatten() {
6214 let name = e.file_name();
6215 let n = name.to_string_lossy();
6216 if n.starts_with(&format!("{stem}-"))
6217 || n.starts_with(&format!("lib{stem}-"))
6218 {
6219 let path = e.path();
6220 let _ = if path.is_dir() {
6221 std::fs::remove_dir_all(&path)
6222 } else {
6223 std::fs::remove_file(&path)
6224 };
6225 }
6226 }
6227 }
6228 }
6229 }
6230
6231 if !output.status.success() {
6232 return Err(format!(
6233 "Execution failed (status={:?} code={:?}):\nstderr: {}\nstdout: {}",
6234 output.status,
6235 output.status.code(),
6236 String::from_utf8_lossy(&output.stderr),
6237 String::from_utf8_lossy(&output.stdout),
6238 ));
6239 }
6240
6241 Ok(String::from_utf8_lossy(&output.stdout).to_string())
6242}
6243
6244pub struct GenuineProjectionResult {
6247 pub source: String,
6250 pub block_entry: String,
6253 pub expr_entry: Option<String>,
6255}
6256
6257fn discover_entry_points(residual: &str, block_prefix: &str, expr_prefix: &str)
6260 -> (String, Option<String>)
6261{
6262 let mut block_entry = String::new();
6263 let mut expr_entry = None;
6264 for line in residual.lines() {
6265 let trimmed = line.trim();
6266 if let Some(rest) = trimmed.strip_prefix("## To ") {
6267 let name = rest.split(" (").next().unwrap_or("").trim();
6269 if name.starts_with(block_prefix) && block_entry.is_empty() {
6270 block_entry = name.to_string();
6271 } else if name.starts_with(expr_prefix) && expr_entry.is_none() {
6272 expr_entry = Some(name.to_string());
6273 }
6274 }
6275 }
6276 (block_entry, expr_entry)
6277}
6278
6279pub fn projection1_source_real(core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
6284 let full_source = if program.contains("## Main") || program.contains("## To ") {
6285 program.to_string()
6286 } else {
6287 format!("## Main\n{}", program)
6288 };
6289
6290 let encoded = encode_program_source(&full_source)
6292 .map_err(|e| format!("Failed to encode program: {:?}", e))?;
6293
6294 let pe_source = pe_source_text();
6296 let decompile_source = decompile_source_text();
6297
6298 let actual_core_types = if core_types.is_empty() { CORE_TYPES_FOR_PE } else { core_types };
6300
6301 let driver = r#"
6302 Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
6303 Let residual be peBlock(encodedMain, state).
6304 Let source be decompileBlock(residual, 0).
6305 Show source.
6306"#;
6307
6308 let combined = format!(
6309 "{}\n{}\n{}\n## Main\n{}\n{}",
6310 actual_core_types,
6311 pe_source,
6312 decompile_source,
6313 encoded,
6314 driver,
6315 );
6316
6317 let raw_residual = run_logos_source(&combined)?;
6319 let trimmed = raw_residual.trim();
6320
6321 if trimmed.is_empty() {
6323 return Ok("## Main\n".to_string());
6324 }
6325
6326 if trimmed.contains("## To ") || trimmed.starts_with("## Main") {
6328 Ok(trimmed.to_string())
6329 } else {
6330 Ok(format!("## Main\n{}", trimmed))
6331 }
6332}
6333
6334pub fn projection1_source_real_fast(core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
6341 let combined = pe_combined_source(core_types, program)?;
6342
6343 let raw_residual = interpret_program(&combined)
6344 .map_err(|e| format!("PE execution failed: {:?}", e))?;
6345 let residual = finish_projection1_residual(raw_residual)?;
6346 Ok(prepend_type_definitions(program, residual))
6351}
6352
6353pub use crate::concurrency::marshal::{decode_value_raw, encode_value_raw};
6354
6355pub fn core_interp_source() -> &'static str {
6360 include_str!("optimize/core_interp.logos")
6361}
6362
6363const PE_DRIVER_CORE: &str = r#"Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
6376Let residual be peBlock(encodedMain, state).
6377Let nl be chr(10).
6378Let mutable output be "".
6379Let specFuncs be peFuncs(state).
6380Let mutable allNames be collectCallNames(residual).
6381Let mutable emitted be a new Map of Text to Bool.
6382Let mutable changed be true.
6383While changed:
6384 Set changed to false.
6385 Let mutable toAdd be a new Seq of Text.
6386 Repeat for fnKey in allNames:
6387 Let fkStr be "{fnKey}".
6388 If emitted contains fkStr:
6389 Let skipE be true.
6390 Otherwise:
6391 Set item fkStr of emitted to true.
6392 Let fkStr2 be "{fnKey}".
6393 If specFuncs contains fkStr2:
6394 Let fdef be item fkStr2 of specFuncs.
6395 Inspect fdef:
6396 When CFuncDef (fn0, ps0, pt0, rt0, body0):
6397 Let children be collectCallNames(body0).
6398 Repeat for child in children:
6399 Let childStr be "{child}".
6400 If not emitted contains childStr:
6401 Push child to toAdd.
6402 Set changed to true.
6403 Otherwise:
6404 Let skipF be true.
6405 Repeat for ta in toAdd:
6406 Push ta to allNames.
6407Repeat for fnKey in allNames:
6408 Let fkStr be "{fnKey}".
6409 If specFuncs contains fkStr:
6410 Let fdef be item fkStr of specFuncs.
6411 Let funcSrc be decompileFunc(fdef).
6412 If the length of funcSrc is greater than 0:
6413 Set output to "{output}{funcSrc}{nl}".
6414Let mainSrc be decompileBlock(residual, 0).
6415Set output to "{output}## Main{nl}{mainSrc}"."#;
6416
6417fn pe_driver_body(last: &str) -> String {
6420 format!("{PE_DRIVER_CORE}\n{last}")
6421}
6422
6423pub fn pe_native_source() -> String {
6424 let core = CORE_TYPES_FOR_PE;
6425 let pe = pe_source_text();
6426 let decompile = decompile_source_text();
6427 let driver_body = pe_driver_body("Let __wrn be writeWireResidual(output).");
6432 let indented: String = driver_body
6437 .lines()
6438 .map(|l| if l.is_empty() { String::new() } else { format!(" {l}") })
6439 .collect::<Vec<_>>()
6440 .join("\n");
6441 let main = format!(
6442 " While true:\n Let __prog be readWireProgram().\n Inspect __prog:\n When CProg (encFuncs, encMain):\n Let encodedMain be encMain.\n Let mutable encodedFuncMap be a new Map of Text to CFunc.\n Repeat for __f in encFuncs:\n Inspect __f:\n When CFuncDef (__nm, __ps, __pt, __rt, __bd):\n Set item __nm of encodedFuncMap to __f.\n Otherwise:\n Let __skip be true.\n{indented}\n Otherwise:\n Let __wrn be writeWireResidual(\"\").\n"
6443 );
6444 format!("{}\n{}\n{}\n## Main\n{}", core, pe, decompile, main)
6445}
6446
6447pub fn pe_cdylib_source() -> String {
6454 let core = CORE_TYPES_FOR_PE;
6455 let pe = pe_source_text();
6456 let decompile = decompile_source_text();
6457 let driver_body = pe_driver_body("Return output.");
6461 let indented: String = driver_body
6462 .lines()
6463 .map(|l| if l.is_empty() { String::new() } else { format!(" {l}") })
6464 .collect::<Vec<_>>()
6465 .join("\n");
6466 let func = format!(
6467 "## To peSpecializeOnce (__prog: CProgram) -> Text:\n Inspect __prog:\n When CProg (encFuncs, encMain):\n Let encodedMain be encMain.\n Let mutable encodedFuncMap be a new Map of Text to CFunc.\n Repeat for __f in encFuncs:\n Inspect __f:\n When CFuncDef (__nm, __ps, __pt, __rt, __bd):\n Set item __nm of encodedFuncMap to __f.\n Otherwise:\n Let __skip be true.\n{indented}\n Otherwise:\n Return \"\".\n"
6468 );
6469 format!("{}\n{}\n{}\n{}\n## Main\n Let __x be 0.\n", core, pe, decompile, func)
6470}
6471
6472use crate::interpreter::RuntimeValue;
6473
6474fn cv_enum(ctor: &str) -> &'static str {
6488 match ctor {
6489 "CProg" => "CProgram",
6490 "CFuncDef" => "CFunc",
6491 "CLiteralPart" | "CExprPart" => "CStringPart",
6492 "CInt" | "CBool" | "CText" | "CFloat" | "CVar" | "CBinOp" | "CNot" | "CCall" | "CIndex"
6493 | "CLen" | "CMapGet" | "CList" | "CTuple" | "CRange" | "CSlice" | "CCopy" | "CContains"
6494 | "CUnion" | "CIntersection" | "COptionSome" | "COptionNone" | "CNewSeq" | "CNewSet"
6495 | "CNew" | "CNewVariant" | "CInterpolatedString" | "CClosure" | "CDuration"
6496 | "CCallExpr" | "CChunkAt" | "CEscExpr" | "CManifestOf" => "CExpr",
6497 "CSelectRecv" | "CSelectTimeout" => "CSelectBranch",
6498 _ => "CStmt",
6499 }
6500}
6501
6502fn cv_ind(ctor: &str, args: Vec<RuntimeValue>) -> RuntimeValue {
6503 RuntimeValue::Inductive(Box::new(crate::interpreter::InductiveValue {
6504 inductive_type: cv_enum(ctor).to_string(),
6505 constructor: ctor.to_string(),
6506 args,
6507 }))
6508}
6509fn cv_text(s: &str) -> RuntimeValue {
6510 RuntimeValue::Text(std::rc::Rc::new(s.to_string()))
6511}
6512fn cv_list(items: Vec<RuntimeValue>) -> RuntimeValue {
6513 RuntimeValue::List(std::rc::Rc::new(std::cell::RefCell::new(
6514 crate::interpreter::ListRepr::from_values(items),
6515 )))
6516}
6517
6518fn cv_binop_str(op: BinaryOpKind) -> &'static str {
6520 match op {
6521 BinaryOpKind::Add => "+",
6522 BinaryOpKind::Subtract => "-",
6523 BinaryOpKind::Multiply => "*",
6524 BinaryOpKind::Divide | BinaryOpKind::ExactDivide => "/",
6525 BinaryOpKind::FloorDivide => "//",
6526 BinaryOpKind::Modulo => "%",
6527 BinaryOpKind::Eq => "==",
6528 BinaryOpKind::NotEq => "!=",
6529 BinaryOpKind::Lt => "<",
6530 BinaryOpKind::Gt => ">",
6531 BinaryOpKind::LtEq => "<=",
6532 BinaryOpKind::GtEq => ">=",
6533 BinaryOpKind::And => "&&",
6534 BinaryOpKind::Or => "||",
6535 BinaryOpKind::Concat => "+",
6536 BinaryOpKind::SeqConcat => "followed by",
6537 BinaryOpKind::ApproxEq => "is approximately",
6538 BinaryOpKind::Pow => "**",
6539 BinaryOpKind::BitXor => "^",
6540 BinaryOpKind::BitAnd => "&",
6541 BinaryOpKind::BitOr => "|",
6542 BinaryOpKind::Shl => "<<",
6543 BinaryOpKind::Shr => ">>",
6544 }
6545}
6546
6547type VCtors = std::collections::HashMap<String, Vec<String>>;
6548
6549trait CoreSink {
6556 type Val;
6557 fn int(n: i64) -> Self::Val;
6558 fn boolean(b: bool) -> Self::Val;
6559 fn float(f: f64) -> Self::Val;
6560 fn text(s: &str) -> Self::Val;
6561 fn list(items: Vec<Self::Val>) -> Self::Val;
6562 fn inductive(ctor: &str, args: Vec<Self::Val>) -> Self::Val;
6563}
6564
6565struct TreeSink;
6567impl CoreSink for TreeSink {
6568 type Val = RuntimeValue;
6569 fn int(n: i64) -> RuntimeValue {
6570 RuntimeValue::Int(n)
6571 }
6572 fn boolean(b: bool) -> RuntimeValue {
6573 RuntimeValue::Bool(b)
6574 }
6575 fn float(f: f64) -> RuntimeValue {
6576 RuntimeValue::Float(f)
6577 }
6578 fn text(s: &str) -> RuntimeValue {
6579 cv_text(s)
6580 }
6581 fn list(items: Vec<RuntimeValue>) -> RuntimeValue {
6582 cv_list(items)
6583 }
6584 fn inductive(ctor: &str, args: Vec<RuntimeValue>) -> RuntimeValue {
6585 cv_ind(ctor, args)
6586 }
6587}
6588
6589struct WireSink;
6595impl CoreSink for WireSink {
6596 type Val = Vec<u8>;
6597 fn int(n: i64) -> Vec<u8> {
6598 let mut v = Vec::with_capacity(4);
6599 v.push(logicaffeine_data::wire::T_INT);
6600 logicaffeine_data::wire::write_uvarint(logicaffeine_data::wire::zigzag(n), &mut v);
6601 v
6602 }
6603 fn boolean(b: bool) -> Vec<u8> {
6604 vec![if b { logicaffeine_data::wire::T_TRUE } else { logicaffeine_data::wire::T_FALSE }]
6605 }
6606 fn float(f: f64) -> Vec<u8> {
6607 let mut v = Vec::with_capacity(9);
6608 v.push(logicaffeine_data::wire::T_FLOAT);
6609 v.extend_from_slice(&f.to_le_bytes());
6610 v
6611 }
6612 fn text(s: &str) -> Vec<u8> {
6613 let mut v = Vec::with_capacity(2 + s.len());
6614 v.push(logicaffeine_data::wire::T_TEXT);
6615 logicaffeine_data::wire::write_str(s, &mut v);
6616 v
6617 }
6618 fn list(items: Vec<Vec<u8>>) -> Vec<u8> {
6619 let mut v = Vec::new();
6620 v.push(logicaffeine_data::wire::T_LIST);
6621 logicaffeine_data::wire::write_uvarint(items.len() as u64, &mut v);
6622 for it in &items {
6623 v.extend_from_slice(it);
6624 }
6625 v
6626 }
6627 fn inductive(ctor: &str, args: Vec<Vec<u8>>) -> Vec<u8> {
6628 let mut v = Vec::new();
6629 logicaffeine_data::wire::write_inductive_header(&mut v, cv_enum(ctor), ctor, args.len() as u64);
6630 for a in &args {
6631 v.extend_from_slice(a);
6632 }
6633 v
6634 }
6635}
6636
6637fn exprs_to_core<S: CoreSink>(args: &[&Expr], interner: &Interner, vctors: &VCtors) -> Option<Vec<S::Val>> {
6638 let mut out = Vec::with_capacity(args.len());
6639 for a in args {
6640 out.push(expr_to_core::<S>(a, interner, vctors)?);
6641 }
6642 Some(out)
6643}
6644
6645fn expr_to_core<S: CoreSink>(expr: &Expr, interner: &Interner, vctors: &VCtors) -> Option<S::Val> {
6646 Some(match expr {
6647 Expr::Literal(Literal::Number(n)) => S::inductive("CInt", vec![S::int(*n)]),
6648 Expr::Literal(Literal::Boolean(b)) => S::inductive("CBool", vec![S::boolean(*b)]),
6649 Expr::Literal(Literal::Text(s)) => S::inductive("CText", vec![S::text(interner.resolve(*s))]),
6650 Expr::Literal(Literal::Float(f)) => S::inductive("CFloat", vec![S::float(*f)]),
6651 Expr::Literal(Literal::Duration(nanos)) => S::inductive(
6655 "CDuration",
6656 vec![S::inductive("CInt", vec![S::int(nanos / 1_000_000)]), S::text("milliseconds")],
6657 ),
6658 Expr::Literal(Literal::Nothing) => S::inductive("CText", vec![S::text("nothing")]),
6659 Expr::Identifier(sym) => S::inductive("CVar", vec![S::text(interner.resolve(*sym))]),
6660 Expr::BinaryOp { op, left, right } => S::inductive(
6661 "CBinOp",
6662 vec![S::text(cv_binop_str(*op)), expr_to_core::<S>(left, interner, vctors)?, expr_to_core::<S>(right, interner, vctors)?],
6663 ),
6664 Expr::Not { operand } => S::inductive("CNot", vec![expr_to_core::<S>(operand, interner, vctors)?]),
6665 Expr::Call { function, args } => {
6666 let fn_name = interner.resolve(*function);
6667 if let Some(field_names) = vctors.get(fn_name) {
6668 let mut fnames: Vec<S::Val> = Vec::with_capacity(args.len());
6670 let mut fvals: Vec<S::Val> = Vec::with_capacity(args.len());
6671 for (i, a) in args.iter().enumerate() {
6672 fnames.push(S::text(field_names.get(i).map(|s| s.as_str()).unwrap_or("value")));
6673 fvals.push(expr_to_core::<S>(a, interner, vctors)?);
6674 }
6675 S::inductive("CNewVariant", vec![S::text(fn_name), S::list(fnames), S::list(fvals)])
6676 } else {
6677 let avals = exprs_to_core::<S>(args, interner, vctors)?;
6678 S::inductive("CCall", vec![S::text(fn_name), S::list(avals)])
6679 }
6680 }
6681 Expr::Index { collection, index } => S::inductive(
6682 "CIndex",
6683 vec![expr_to_core::<S>(collection, interner, vctors)?, expr_to_core::<S>(index, interner, vctors)?],
6684 ),
6685 Expr::Length { collection } => S::inductive("CLen", vec![expr_to_core::<S>(collection, interner, vctors)?]),
6686 Expr::FieldAccess { object, field } => S::inductive(
6687 "CMapGet",
6688 vec![expr_to_core::<S>(object, interner, vctors)?, S::inductive("CText", vec![S::text(interner.resolve(*field))])],
6689 ),
6690 Expr::List(elems) => {
6691 let mut items: Vec<S::Val> = Vec::with_capacity(elems.len());
6692 for e in elems.iter() {
6693 items.push(expr_to_core::<S>(e, interner, vctors)?);
6694 }
6695 S::inductive("CList", vec![S::list(items)])
6696 }
6697 Expr::Tuple(elems) => {
6698 let mut items: Vec<S::Val> = Vec::with_capacity(elems.len());
6699 for e in elems.iter() {
6700 items.push(expr_to_core::<S>(e, interner, vctors)?);
6701 }
6702 S::inductive("CTuple", vec![S::list(items)])
6703 }
6704 Expr::Range { start, end } => S::inductive("CRange", vec![expr_to_core::<S>(start, interner, vctors)?, expr_to_core::<S>(end, interner, vctors)?]),
6705 Expr::Slice { collection, start, end } => S::inductive(
6706 "CSlice",
6707 vec![expr_to_core::<S>(collection, interner, vctors)?, expr_to_core::<S>(start, interner, vctors)?, expr_to_core::<S>(end, interner, vctors)?],
6708 ),
6709 Expr::Copy { expr } => S::inductive("CCopy", vec![expr_to_core::<S>(expr, interner, vctors)?]),
6710 Expr::Contains { collection, value } => S::inductive("CContains", vec![expr_to_core::<S>(collection, interner, vctors)?, expr_to_core::<S>(value, interner, vctors)?]),
6711 Expr::Union { left, right } => S::inductive("CUnion", vec![expr_to_core::<S>(left, interner, vctors)?, expr_to_core::<S>(right, interner, vctors)?]),
6712 Expr::Intersection { left, right } => S::inductive("CIntersection", vec![expr_to_core::<S>(left, interner, vctors)?, expr_to_core::<S>(right, interner, vctors)?]),
6713 Expr::OptionSome { value } => S::inductive("COptionSome", vec![expr_to_core::<S>(value, interner, vctors)?]),
6714 Expr::OptionNone => S::inductive("COptionNone", vec![]),
6715 Expr::InterpolatedString(parts) => {
6716 if parts.is_empty() {
6717 S::inductive("CText", vec![S::text("")])
6718 } else {
6719 let mut pvals: Vec<S::Val> = Vec::with_capacity(parts.len());
6720 for part in parts {
6721 pvals.push(match part {
6722 StringPart::Literal(sym) => S::inductive("CLiteralPart", vec![S::text(interner.resolve(*sym))]),
6723 StringPart::Expr { value, .. } => S::inductive("CExprPart", vec![expr_to_core::<S>(value, interner, vctors)?]),
6724 });
6725 }
6726 S::inductive("CInterpolatedString", vec![S::list(pvals)])
6727 }
6728 }
6729 Expr::NewVariant { variant, fields, .. } => {
6730 let mut fnames: Vec<S::Val> = Vec::with_capacity(fields.len());
6731 let mut fvals: Vec<S::Val> = Vec::with_capacity(fields.len());
6732 for (fname, fexpr) in fields {
6733 fnames.push(S::text(interner.resolve(*fname)));
6734 fvals.push(expr_to_core::<S>(fexpr, interner, vctors)?);
6735 }
6736 S::inductive("CNewVariant", vec![S::text(interner.resolve(*variant)), S::list(fnames), S::list(fvals)])
6737 }
6738 Expr::New { type_name, init_fields, .. } => {
6739 let tn = interner.resolve(*type_name);
6740 if tn == "Seq" || tn == "List" {
6741 S::inductive("CNewSeq", vec![])
6742 } else if tn == "Set" {
6743 S::inductive("CNewSet", vec![])
6744 } else if tn == "Map" || tn.starts_with("Map ") {
6745 S::inductive("CNew", vec![S::text("Map"), S::list(vec![]), S::list(vec![])])
6746 } else if init_fields.is_empty() {
6747 S::inductive("CNewVariant", vec![S::text(tn), S::list(vec![]), S::list(vec![])])
6748 } else {
6749 let mut fnames: Vec<S::Val> = Vec::with_capacity(init_fields.len());
6750 let mut fvals: Vec<S::Val> = Vec::with_capacity(init_fields.len());
6751 for (fname, fexpr) in init_fields {
6752 fnames.push(S::text(interner.resolve(*fname)));
6753 fvals.push(expr_to_core::<S>(fexpr, interner, vctors)?);
6754 }
6755 S::inductive("CNew", vec![S::text(tn), S::list(fnames), S::list(fvals)])
6756 }
6757 }
6758 Expr::Closure { params, body, .. } => {
6761 let mut bound: std::collections::HashSet<String> = std::collections::HashSet::new();
6762 let mut param_vals: Vec<S::Val> = Vec::with_capacity(params.len());
6763 for (sym, _) in params {
6764 let name = interner.resolve(*sym);
6765 bound.insert(name.to_string());
6766 param_vals.push(S::text(name));
6767 }
6768 let body_vals: Vec<S::Val> = match body {
6769 ClosureBody::Expression(e) => {
6770 vec![S::inductive("CReturn", vec![expr_to_core::<S>(e, interner, vctors)?])]
6771 }
6772 ClosureBody::Block(stmts) => {
6773 let mut bv: Vec<S::Val> = Vec::with_capacity(stmts.len());
6774 for s in stmts.iter() {
6775 if matches!(s, Stmt::Inspect { .. } | Stmt::Repeat { .. }) {
6778 return None;
6779 }
6780 bv.push(stmt_to_core::<S>(s, interner, vctors)?);
6781 }
6782 bv
6783 }
6784 };
6785 let mut free: Vec<String> =
6786 collect_free_vars_expr(expr, interner, &bound).into_iter().collect();
6787 free.sort();
6788 let cap_vals: Vec<S::Val> = free.iter().map(|fv| S::text(fv)).collect();
6789 S::inductive("CClosure", vec![S::list(param_vals), S::list(body_vals), S::list(cap_vals)])
6790 }
6791 Expr::CallExpr { callee, args } => {
6793 let mut avals: Vec<S::Val> = Vec::with_capacity(args.len());
6794 for a in args.iter() {
6795 avals.push(expr_to_core::<S>(a, interner, vctors)?);
6796 }
6797 S::inductive("CCallExpr", vec![expr_to_core::<S>(callee, interner, vctors)?, S::list(avals)])
6798 }
6799 Expr::ChunkAt { index, zone } => S::inductive(
6800 "CChunkAt",
6801 vec![expr_to_core::<S>(index, interner, vctors)?, expr_to_core::<S>(zone, interner, vctors)?],
6802 ),
6803 Expr::ManifestOf { zone } => S::inductive("CManifestOf", vec![expr_to_core::<S>(zone, interner, vctors)?]),
6804 Expr::Escape { code, .. } => S::inductive("CEscExpr", vec![S::text(interner.resolve(*code))]),
6805 Expr::Give { value } => expr_to_core::<S>(value, interner, vctors)?,
6808 Expr::WithCapacity { value, .. } => expr_to_core::<S>(value, interner, vctors)?,
6809 _ => return None,
6810 })
6811}
6812
6813fn stmt_to_core<S: CoreSink>(stmt: &Stmt, interner: &Interner, vctors: &VCtors) -> Option<S::Val> {
6814 Some(match stmt {
6815 Stmt::Let { var, value, .. } => S::inductive("CLet", vec![S::text(interner.resolve(*var)), expr_to_core::<S>(value, interner, vctors)?]),
6816 Stmt::Set { target, value } => S::inductive("CSet", vec![S::text(interner.resolve(*target)), expr_to_core::<S>(value, interner, vctors)?]),
6817 Stmt::Show { object, .. } => S::inductive("CShow", vec![expr_to_core::<S>(object, interner, vctors)?]),
6818 Stmt::SetIndex { collection, index, value } => S::inductive(
6819 "CSetIdx",
6820 vec![S::text(&extract_ident_name(collection, interner)), expr_to_core::<S>(index, interner, vctors)?, expr_to_core::<S>(value, interner, vctors)?],
6821 ),
6822 Stmt::SetField { object, field, value } => S::inductive(
6823 "CMapSet",
6824 vec![
6825 S::text(&extract_ident_name(object, interner)),
6826 S::inductive("CText", vec![S::text(interner.resolve(*field))]),
6827 expr_to_core::<S>(value, interner, vctors)?,
6828 ],
6829 ),
6830 Stmt::Pop { collection, .. } => S::inductive("CPop", vec![S::text(&extract_ident_name(collection, interner))]),
6831 Stmt::Give { object, recipient } => S::inductive(
6832 "CGive",
6833 vec![expr_to_core::<S>(object, interner, vctors)?, S::text(&extract_ident_name(recipient, interner))],
6834 ),
6835 Stmt::Sleep { milliseconds } => S::inductive("CSleep", vec![expr_to_core::<S>(milliseconds, interner, vctors)?]),
6836 Stmt::RuntimeAssert { condition, hard } => S::inductive(
6837 if *hard { "CHardAssert" } else { "CRuntimeAssert" },
6838 vec![expr_to_core::<S>(condition, interner, vctors)?, S::inductive("CText", vec![S::text("assertion failed")])],
6839 ),
6840 Stmt::Return { value } => {
6841 let e = match value {
6842 Some(v) => expr_to_core::<S>(v, interner, vctors)?,
6843 None => S::inductive("CInt", vec![S::int(0)]),
6844 };
6845 S::inductive("CReturn", vec![e])
6846 }
6847 Stmt::Break => S::inductive("CBreak", vec![]),
6848 Stmt::If { cond, then_block, else_block } => {
6849 let cond_v = expr_to_core::<S>(cond, interner, vctors)?;
6850 let then_s: Vec<&Stmt> = then_block.iter().collect();
6851 let then_v = stmt_list_to_core::<S>(&then_s, interner, vctors)?;
6852 let else_v = match else_block {
6853 Some(els) => {
6854 let else_s: Vec<&Stmt> = els.iter().collect();
6855 stmt_list_to_core::<S>(&else_s, interner, vctors)?
6856 }
6857 None => S::list(vec![]),
6858 };
6859 S::inductive("CIf", vec![cond_v, then_v, else_v])
6860 }
6861 Stmt::While { cond, body, .. } => {
6862 let cond_v = expr_to_core::<S>(cond, interner, vctors)?;
6863 let body_s: Vec<&Stmt> = body.iter().collect();
6864 S::inductive("CWhile", vec![cond_v, stmt_list_to_core::<S>(&body_s, interner, vctors)?])
6865 }
6866 Stmt::Call { function, args } => {
6867 let mut avals: Vec<S::Val> = Vec::with_capacity(args.len());
6868 for a in args {
6869 avals.push(expr_to_core::<S>(a, interner, vctors)?);
6870 }
6871 S::inductive("CCallS", vec![S::text(interner.resolve(*function)), S::list(avals)])
6872 }
6873 Stmt::Push { value, collection } => S::inductive(
6874 "CPush",
6875 vec![expr_to_core::<S>(value, interner, vctors)?, S::text(&extract_ident_name(collection, interner))],
6876 ),
6877 Stmt::Repeat { pattern, iterable, body, .. } => {
6878 let loop_var = match pattern {
6879 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
6880 Pattern::Tuple(syms) => syms.first().map(|s| interner.resolve(*s).to_string()).unwrap_or_else(|| "item".to_string()),
6881 };
6882 let body_s: Vec<&Stmt> = body.iter().collect();
6883 let body_v = stmt_list_to_core::<S>(&body_s, interner, vctors)?;
6884 if let Expr::Range { start, end } = iterable {
6885 S::inductive(
6886 "CRepeatRange",
6887 vec![S::text(&loop_var), expr_to_core::<S>(start, interner, vctors)?, expr_to_core::<S>(end, interner, vctors)?, body_v],
6888 )
6889 } else {
6890 S::inductive("CRepeat", vec![S::text(&loop_var), expr_to_core::<S>(iterable, interner, vctors)?, body_v])
6891 }
6892 }
6893 Stmt::Add { value, collection } => S::inductive(
6895 "CAdd",
6896 vec![expr_to_core::<S>(value, interner, vctors)?, S::text(&extract_ident_name(collection, interner))],
6897 ),
6898 Stmt::Remove { value, collection } => S::inductive(
6899 "CRemove",
6900 vec![expr_to_core::<S>(value, interner, vctors)?, S::text(&extract_ident_name(collection, interner))],
6901 ),
6902 Stmt::MergeCrdt { source, target } => S::inductive(
6903 "CMerge",
6904 vec![S::text(&extract_ident_name(target, interner)), expr_to_core::<S>(source, interner, vctors)?],
6905 ),
6906 Stmt::IncreaseCrdt { object, field, amount } => S::inductive(
6907 "CIncrease",
6908 vec![
6909 S::text(&format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field))),
6910 expr_to_core::<S>(amount, interner, vctors)?,
6911 ],
6912 ),
6913 Stmt::DecreaseCrdt { object, field, amount } => S::inductive(
6914 "CDecrease",
6915 vec![
6916 S::text(&format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field))),
6917 expr_to_core::<S>(amount, interner, vctors)?,
6918 ],
6919 ),
6920 Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
6922 let func_name = interner.resolve(*function);
6923 let mut avals: Vec<S::Val> = Vec::with_capacity(args.len());
6924 for a in args {
6925 avals.push(expr_to_core::<S>(a, interner, vctors)?);
6926 }
6927 let call = S::inductive("CCallS", vec![S::text(func_name), S::list(avals)]);
6928 let body = S::list(vec![call]);
6929 let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
6930 interner.resolve(*handle).to_string()
6931 } else {
6932 "_task".to_string()
6933 };
6934 S::inductive("CLaunchTask", vec![body, S::text(&handle_name)])
6935 }
6936 Stmt::Concurrent { tasks } => {
6940 let mut branches: Vec<S::Val> = Vec::with_capacity(tasks.len());
6941 for task in tasks.iter() {
6942 if matches!(task, Stmt::Inspect { .. } | Stmt::Repeat { .. }) {
6943 return None;
6944 }
6945 branches.push(S::list(vec![stmt_to_core::<S>(task, interner, vctors)?]));
6946 }
6947 S::inductive("CConcurrent", vec![S::list(branches)])
6948 }
6949 Stmt::Parallel { tasks } => {
6950 let mut branches: Vec<S::Val> = Vec::with_capacity(tasks.len());
6951 for task in tasks.iter() {
6952 if matches!(task, Stmt::Inspect { .. } | Stmt::Repeat { .. }) {
6953 return None;
6954 }
6955 branches.push(S::list(vec![stmt_to_core::<S>(task, interner, vctors)?]));
6956 }
6957 S::inductive("CParallel", vec![S::list(branches)])
6958 }
6959 Stmt::AppendToSequence { sequence, value } => S::inductive(
6962 "CAppendToSeq",
6963 vec![S::text(&extract_ident_name(sequence, interner)), expr_to_core::<S>(value, interner, vctors)?],
6964 ),
6965 Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
6967 let cap = capacity.unwrap_or(32);
6968 S::inductive(
6969 "CCreatePipe",
6970 vec![S::text(interner.resolve(*pipe_var)), S::inductive("CInt", vec![S::int(cap as i64)])],
6971 )
6972 }
6973 Stmt::SendPipe { value, pipe } => S::inductive(
6974 "CSendPipe",
6975 vec![S::text(&pipe_ident_name(pipe, interner)), expr_to_core::<S>(value, interner, vctors)?],
6976 ),
6977 Stmt::ReceivePipe { var: recv_var, pipe } => S::inductive(
6978 "CReceivePipe",
6979 vec![S::text(&pipe_ident_name(pipe, interner)), S::text(interner.resolve(*recv_var))],
6980 ),
6981 Stmt::TrySendPipe { value, pipe, .. } => S::inductive(
6982 "CTrySendPipe",
6983 vec![S::text(&pipe_ident_name(pipe, interner)), expr_to_core::<S>(value, interner, vctors)?],
6984 ),
6985 Stmt::TryReceivePipe { var: recv_var, pipe } => S::inductive(
6986 "CTryReceivePipe",
6987 vec![S::text(&pipe_ident_name(pipe, interner)), S::text(interner.resolve(*recv_var))],
6988 ),
6989 Stmt::Select { branches } => {
6992 let mut bvals: Vec<S::Val> = Vec::with_capacity(branches.len());
6993 for branch in branches.iter() {
6994 match branch {
6995 SelectBranch::Receive { var: recv_var, pipe, body } => bvals.push(S::inductive(
6996 "CSelectRecv",
6997 vec![
6998 S::text(&pipe_ident_name(pipe, interner)),
6999 S::text(interner.resolve(*recv_var)),
7000 select_body_to_core::<S>(body, interner, vctors)?,
7001 ],
7002 )),
7003 SelectBranch::Timeout { milliseconds, body } => bvals.push(S::inductive(
7004 "CSelectTimeout",
7005 vec![
7006 expr_to_core::<S>(milliseconds, interner, vctors)?,
7007 select_body_to_core::<S>(body, interner, vctors)?,
7008 ],
7009 )),
7010 }
7011 }
7012 S::inductive("CSelect", vec![S::list(bvals)])
7013 }
7014 Stmt::Assert { .. } => S::inductive("CAssert", vec![S::inductive("CBool", vec![S::boolean(true)])]),
7017 Stmt::Check { source_text, .. } => S::inductive(
7018 "CCheck",
7019 vec![S::inductive("CBool", vec![S::boolean(true)]), S::inductive("CText", vec![S::text(source_text)])],
7020 ),
7021 Stmt::Trust { justification, .. } => S::inductive(
7022 "CTrust",
7023 vec![S::inductive("CBool", vec![S::boolean(true)]), S::text(interner.resolve(*justification))],
7024 ),
7025 Stmt::Require { crate_name, .. } => S::inductive("CRequire", vec![S::text(interner.resolve(*crate_name))]),
7026 Stmt::Escape { code, .. } => S::inductive("CEscStmt", vec![S::text(interner.resolve(*code))]),
7028 Stmt::Splice { body } => {
7030 let body_refs: Vec<&Stmt> = body.iter().collect();
7031 S::inductive(
7032 "CIf",
7033 vec![
7034 S::inductive("CBool", vec![S::boolean(true)]),
7035 stmt_list_to_core::<S>(&body_refs, interner, vctors)?,
7036 S::list(vec![]),
7037 ],
7038 )
7039 }
7040 Stmt::StopTask { handle } => S::inductive("CStopTask", vec![expr_to_core::<S>(handle, interner, vctors)?]),
7042 Stmt::Spawn { agent_type, name } => S::inductive(
7043 "CSpawn",
7044 vec![S::text(interner.resolve(*agent_type)), S::text(interner.resolve(*name))],
7045 ),
7046 Stmt::Zone { name, body, .. } => S::inductive(
7048 "CZone",
7049 vec![S::text(interner.resolve(*name)), S::text("heap"), select_body_to_core::<S>(body, interner, vctors)?],
7050 ),
7051 Stmt::ResolveConflict { object, field, .. } => S::inductive(
7053 "CResolve",
7054 vec![S::text(&format!("{}'s {}", extract_ident_name(object, interner), interner.resolve(*field)))],
7055 ),
7056 Stmt::Sync { var: sync_var, topic } => S::inductive(
7058 "CSync",
7059 vec![S::text(interner.resolve(*sync_var)), expr_to_core::<S>(topic, interner, vctors)?],
7060 ),
7061 Stmt::Mount { var: mount_var, path } => S::inductive(
7062 "CMount",
7063 vec![S::text(interner.resolve(*mount_var)), expr_to_core::<S>(path, interner, vctors)?],
7064 ),
7065 Stmt::ReadFrom { var: read_var, source } => {
7067 let var_name = interner.resolve(*read_var);
7068 match source {
7069 ReadSource::Console => S::inductive("CReadConsole", vec![S::text(var_name)]),
7070 ReadSource::File(path_expr) => S::inductive(
7071 "CReadFile",
7072 vec![expr_to_core::<S>(path_expr, interner, vctors)?, S::text(var_name)],
7073 ),
7074 }
7075 }
7076 Stmt::WriteFile { content, path } => S::inductive(
7077 "CWriteFile",
7078 vec![expr_to_core::<S>(path, interner, vctors)?, expr_to_core::<S>(content, interner, vctors)?],
7079 ),
7080 Stmt::ConnectTo { address, .. } => S::inductive(
7082 "CConnectTo",
7083 vec![expr_to_core::<S>(address, interner, vctors)?, S::text("conn")],
7084 ),
7085 Stmt::Listen { address, .. } => S::inductive(
7086 "CListen",
7087 vec![expr_to_core::<S>(address, interner, vctors)?, S::text("default")],
7088 ),
7089 Stmt::SendMessage { message, destination, .. } => S::inductive(
7090 "CSendMessage",
7091 vec![expr_to_core::<S>(destination, interner, vctors)?, expr_to_core::<S>(message, interner, vctors)?],
7092 ),
7093 Stmt::AwaitMessage { into, .. } => S::inductive("CAwaitMessage", vec![S::text(interner.resolve(*into))]),
7094 Stmt::StreamMessage { values, destination } => S::inductive(
7095 "CStreamMessage",
7096 vec![expr_to_core::<S>(destination, interner, vctors)?, expr_to_core::<S>(values, interner, vctors)?],
7097 ),
7098 Stmt::LetPeerAgent { var: pa_var, address } => S::inductive(
7099 "CConnectTo",
7100 vec![expr_to_core::<S>(address, interner, vctors)?, S::text(interner.resolve(*pa_var))],
7101 ),
7102 _ => return None,
7103 })
7104}
7105
7106fn pipe_ident_name(pipe: &Expr, interner: &Interner) -> String {
7108 match pipe {
7109 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
7110 _ => "pipe".to_string(),
7111 }
7112}
7113
7114fn select_body_to_core<S: CoreSink>(body: &[Stmt], interner: &Interner, vctors: &VCtors) -> Option<S::Val> {
7117 let mut items: Vec<S::Val> = Vec::with_capacity(body.len());
7118 for s in body.iter() {
7119 if matches!(s, Stmt::Inspect { .. } | Stmt::Repeat { .. }) {
7120 return None;
7121 }
7122 items.push(stmt_to_core::<S>(s, interner, vctors)?);
7123 }
7124 Some(S::list(items))
7125}
7126
7127fn stmt_list_to_core<S: CoreSink>(stmts: &[&Stmt], interner: &Interner, vctors: &VCtors) -> Option<S::Val> {
7128 Some(S::list(stmt_list_to_vec::<S>(stmts, interner, vctors)?))
7129}
7130
7131fn stmt_list_to_vec<S: CoreSink>(stmts: &[&Stmt], interner: &Interner, vctors: &VCtors) -> Option<Vec<S::Val>> {
7135 let mut out: Vec<S::Val> = Vec::with_capacity(stmts.len());
7136 for s in stmts {
7137 match s {
7138 Stmt::Inspect { .. } => inspect_to_cif::<S>(s, &mut out, interner, vctors)?,
7139 Stmt::Add { collection, .. } | Stmt::Remove { collection, .. } => {
7144 if let Some(base) = crdt_base_var(collection, interner) {
7145 out.push(S::inductive("CForceDynamic", vec![S::text(&base)]));
7146 }
7147 out.push(stmt_to_core::<S>(s, interner, vctors)?);
7148 }
7149 Stmt::AppendToSequence { sequence, .. } => {
7150 if let Some(base) = crdt_base_var(sequence, interner) {
7151 out.push(S::inductive("CForceDynamic", vec![S::text(&base)]));
7152 }
7153 out.push(stmt_to_core::<S>(s, interner, vctors)?);
7154 }
7155 Stmt::IncreaseCrdt { object, .. } | Stmt::DecreaseCrdt { object, .. } | Stmt::ResolveConflict { object, .. } => {
7156 if matches!(object, Expr::Identifier(_) | Expr::FieldAccess { .. }) {
7157 out.push(S::inductive("CForceDynamic", vec![S::text(&crdt_base_var_root(object, interner))]));
7158 }
7159 out.push(stmt_to_core::<S>(s, interner, vctors)?);
7160 }
7161 Stmt::MergeCrdt { target, .. } => {
7162 if matches!(target, Expr::Identifier(_) | Expr::FieldAccess { .. }) {
7163 out.push(S::inductive("CForceDynamic", vec![S::text(&crdt_base_var_root(target, interner))]));
7164 }
7165 out.push(stmt_to_core::<S>(s, interner, vctors)?);
7166 }
7167 _ => out.push(stmt_to_core::<S>(s, interner, vctors)?),
7168 }
7169 }
7170 Some(out)
7171}
7172
7173thread_local! {
7174 static INSPECT_OTHERWISE_IDX: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
7182}
7183
7184fn reset_inspect_otherwise_idx() {
7185 INSPECT_OTHERWISE_IDX.with(|c| c.set(0));
7186}
7187
7188pub fn next_inspect_otherwise_idx() -> usize {
7192 INSPECT_OTHERWISE_IDX.with(|c| {
7193 let v = c.get();
7194 c.set(v + 1);
7195 v
7196 })
7197}
7198
7199fn inspect_to_cif<S: CoreSink>(stmt: &Stmt, out: &mut Vec<S::Val>, interner: &Interner, vctors: &VCtors) -> Option<()> {
7207 let Stmt::Inspect { target, arms, .. } = stmt else {
7208 return None;
7209 };
7210 let has_otherwise = arms.iter().any(|a| a.variant.is_none());
7211 let has_variant = arms.iter().any(|a| a.variant.is_some());
7212
7213 if !has_variant {
7215 for arm in arms {
7216 if arm.variant.is_none() {
7217 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
7218 out.extend(stmt_list_to_vec::<S>(&body_refs, interner, vctors)?);
7219 }
7220 }
7221 return Some(());
7222 }
7223
7224 let matched_name = if has_otherwise {
7227 let name = format!("__inspectMatched_{}", next_inspect_otherwise_idx());
7228 out.push(S::inductive(
7229 "CLet",
7230 vec![S::text(&name), S::inductive("CBool", vec![S::boolean(false)])],
7231 ));
7232 Some(name)
7233 } else {
7234 None
7235 };
7236
7237 for arm in arms {
7238 if arm.variant.is_none() {
7239 continue;
7240 }
7241 let variant_name = interner.resolve(arm.variant.unwrap());
7242 let tag_get = S::inductive(
7243 "CMapGet",
7244 vec![expr_to_core::<S>(target, interner, vctors)?, S::inductive("CText", vec![S::text("__tag")])],
7245 );
7246 let cond = S::inductive(
7247 "CBinOp",
7248 vec![S::text("=="), tag_get, S::inductive("CText", vec![S::text(variant_name)])],
7249 );
7250 let mut then_items: Vec<S::Val> = Vec::new();
7251 if let Some(ref mname) = matched_name {
7252 then_items.push(S::inductive(
7253 "CSet",
7254 vec![S::text(mname), S::inductive("CBool", vec![S::boolean(true)])],
7255 ));
7256 }
7257 for (field_name, binding_name) in &arm.bindings {
7258 let field_str = interner.resolve(*field_name);
7259 let bind_str = interner.resolve(*binding_name);
7260 let fget = S::inductive(
7261 "CMapGet",
7262 vec![expr_to_core::<S>(target, interner, vctors)?, S::inductive("CText", vec![S::text(field_str)])],
7263 );
7264 then_items.push(S::inductive("CLet", vec![S::text(bind_str), fget]));
7265 }
7266 let body_refs: Vec<&Stmt> = arm.body.iter().collect();
7267 then_items.extend(stmt_list_to_vec::<S>(&body_refs, interner, vctors)?);
7268 out.push(S::inductive("CIf", vec![cond, S::list(then_items), S::list(vec![])]));
7269 }
7270
7271 if let Some(ref mname) = matched_name {
7273 let not_matched = S::inductive("CNot", vec![S::inductive("CVar", vec![S::text(mname)])]);
7274 let mut otherwise_body: Vec<&Stmt> = Vec::new();
7275 for arm in arms {
7276 if arm.variant.is_none() {
7277 otherwise_body = arm.body.iter().collect();
7278 }
7279 }
7280 let ow = stmt_list_to_core::<S>(&otherwise_body, interner, vctors)?;
7281 out.push(S::inductive("CIf", vec![not_matched, ow, S::list(vec![])]));
7282 }
7283 Some(())
7284}
7285
7286pub fn program_covered_by_native_builder(program: &str) -> bool {
7289 program_to_core_value(program).is_some()
7290}
7291
7292fn program_to_core_value(program: &str) -> Option<RuntimeValue> {
7295 program_to_core::<TreeSink>(program)
7296}
7297
7298fn program_to_core<S: CoreSink>(program: &str) -> Option<S::Val> {
7304 reset_inspect_otherwise_idx(); let full_source = if program.contains("## Main") || program.contains("## To ") {
7306 program.to_string()
7307 } else {
7308 format!("## Main\n{}", program)
7309 };
7310 let mut interner = Interner::new();
7311 let mut lexer = Lexer::new(&full_source, &mut interner);
7312 let tokens = lexer.tokenize();
7313 let type_registry = {
7314 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
7315 discovery.run_full().types
7316 };
7317 let mut vctors: VCtors = std::collections::HashMap::new();
7318 for (_t, def) in type_registry.iter_types() {
7319 if let crate::analysis::TypeDef::Enum { variants, .. } = def {
7320 for v in variants {
7321 let fnames: Vec<String> = v.fields.iter().map(|f| interner.resolve(f.name).to_string()).collect();
7322 vctors.insert(interner.resolve(v.name).to_string(), fnames);
7323 }
7324 }
7325 }
7326 let mut world_state = WorldState::new();
7327 let expr_arena = Arena::new();
7328 let term_arena = Arena::new();
7329 let np_arena = Arena::new();
7330 let sym_arena = Arena::new();
7331 let role_arena = Arena::new();
7332 let pp_arena = Arena::new();
7333 let stmt_arena: Arena<Stmt> = Arena::new();
7334 let imperative_expr_arena: Arena<Expr> = Arena::new();
7335 let type_expr_arena: Arena<TypeExpr> = Arena::new();
7336 let ast_ctx = AstContext::with_types(
7337 &expr_arena, &term_arena, &np_arena, &sym_arena, &role_arena, &pp_arena,
7338 &stmt_arena, &imperative_expr_arena, &type_expr_arena,
7339 );
7340 let mut parser = crate::parser::Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
7341 let stmts = parser.parse_program().ok()?;
7342 build_core::<S>(&stmts, &interner, &vctors)
7343}
7344
7345fn build_core<S: CoreSink>(stmts: &[Stmt], interner: &Interner, vctors: &VCtors) -> Option<S::Val> {
7351 let mut funcs: Vec<S::Val> = Vec::new();
7352 let mut main_stmts: Vec<&Stmt> = Vec::new();
7353 for stmt in stmts {
7354 if let Stmt::FunctionDef { name, params, body, return_type, is_native, .. } = stmt {
7355 if *is_native {
7356 continue;
7357 }
7358 let fname = interner.resolve(*name).to_string();
7359 let pnames: Vec<S::Val> = params.iter().map(|(n, _)| S::text(interner.resolve(*n))).collect();
7360 let ptypes: Vec<S::Val> = params.iter().map(|(_, ty)| S::text(&decompile_type_expr(ty, interner))).collect();
7361 let ret = return_type.map(|rt| decompile_type_expr(rt, interner)).unwrap_or_else(|| "Nothing".to_string());
7362 let body_s: Vec<&Stmt> = body.iter().collect();
7363 let body_v = stmt_list_to_core::<S>(&body_s, interner, vctors)?;
7364 funcs.push(S::inductive("CFuncDef", vec![S::text(&fname), S::list(pnames), S::list(ptypes), S::text(&ret), body_v]));
7365 } else if matches!(
7366 stmt,
7367 Stmt::StructDef { .. } | Stmt::Theorem(_) | Stmt::Definition(_) | Stmt::Axiom(_) | Stmt::Theory(_)
7368 ) {
7369 } else {
7375 main_stmts.push(stmt);
7376 }
7377 }
7378 let main_v = stmt_list_to_core::<S>(&main_stmts, interner, vctors)?;
7379 Some(S::inductive("CProg", vec![S::list(funcs), main_v]))
7380}
7381
7382pub fn program_to_core_wire_bytes(program: &str) -> Result<Vec<u8>, String> {
7390 if let Some(bytes) = program_to_core::<WireSink>(program) {
7391 return Ok(bytes);
7392 }
7393 program_to_core_wire_bytes_via_interpreter(program)
7394}
7395
7396pub fn stmts_to_core_wire_bytes(
7404 stmts: &[Stmt],
7405 interner: &Interner,
7406 variant_fields: &std::collections::HashMap<String, Vec<String>>,
7407) -> Option<Vec<u8>> {
7408 build_core::<WireSink>(stmts, interner, variant_fields)
7409}
7410
7411pub fn program_to_core_wire_bytes_two_pass(program: &str) -> Option<Vec<u8>> {
7417 program_to_core::<TreeSink>(program).and_then(|v| encode_value_raw(&v).ok())
7418}
7419
7420pub fn program_to_core_wire_bytes_via_interpreter(program: &str) -> Result<Vec<u8>, String> {
7425 let full_source = if program.contains("## Main") || program.contains("## To ") {
7426 program.to_string()
7427 } else {
7428 format!("## Main\n{}", program)
7429 };
7430 let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode program: {:?}", e))?;
7431 let driver = " Let prog be a new CProg with funcs encodedFuncSeq and main encodedMain.\n Let wb be wireBytes(prog).\n Repeat for b in wb:\n Show \"{b}\".\n";
7432 let combined = format!("{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, encoded, driver);
7433 let out = interpret_program(&combined).map_err(|e| format!("wire-encode run failed: {:?}", e))?;
7434 let mut bytes = Vec::new();
7435 for line in out.lines() {
7436 let t = line.trim();
7437 if t.is_empty() {
7438 continue;
7439 }
7440 let n: u16 = t.parse().map_err(|_| format!("wireBytes emitted a non-byte line: {:?}", t))?;
7441 bytes.push(n as u8);
7442 }
7443 Ok(bytes)
7444}
7445
7446#[cfg(not(target_arch = "wasm32"))]
7451thread_local! {
7452 static NATIVE_PE_BIN: std::cell::RefCell<Option<std::path::PathBuf>> = const { std::cell::RefCell::new(None) };
7457}
7458
7459#[cfg(not(target_arch = "wasm32"))]
7460pub fn build_native_pe_binary() -> Result<std::path::PathBuf, String> {
7461 if let Some(p) = NATIVE_PE_BIN.with(|c| c.borrow().clone()) {
7464 if p.exists() {
7465 return Ok(p);
7466 }
7467 }
7468 let src = pe_native_source();
7469 let key = aot_cache_key(&src);
7472 let base = std::env::temp_dir().join("logos_native_pe");
7473 std::fs::create_dir_all(&base).map_err(|e| e.to_string())?;
7474 let proj = base.join(format!("pe_{key}"));
7475 let bin = proj.join("target").join("release").join("logos_native_pe");
7476 if bin.exists() {
7477 NATIVE_PE_BIN.with(|c| *c.borrow_mut() = Some(bin.clone()));
7478 return Ok(bin);
7479 }
7480 let rust = compile_program_full_with_wire(&src)
7481 .map_err(|e| format!("native PE compile: {:?}", e))?
7482 .rust_code;
7483 std::fs::create_dir_all(proj.join("src")).map_err(|e| e.to_string())?;
7484 let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
7485 let root = manifest.parent().unwrap().parent().unwrap();
7486 std::fs::write(
7487 proj.join("Cargo.toml"),
7488 format!(
7489 "[package]\nname=\"logos_native_pe\"\nversion=\"0.1.0\"\nedition=\"2021\"\n[dependencies]\nlogicaffeine-data={{path=\"{r}/crates/logicaffeine_data\"}}\nlogicaffeine-system={{path=\"{r}/crates/logicaffeine_system\",features=[\"full\"]}}\ntokio={{version=\"1\",features=[\"rt-multi-thread\",\"macros\"]}}\nserde={{version=\"1\",features=[\"derive\"]}}\nrayon=\"1\"\n[profile.release]\nopt-level=3\n",
7490 r = root.display()
7491 ),
7492 )
7493 .map_err(|e| e.to_string())?;
7494 std::fs::write(proj.join("src/main.rs"), &rust).map_err(|e| e.to_string())?;
7495 std::fs::copy(root.join("Cargo.lock"), proj.join("Cargo.lock")).map_err(|e| e.to_string())?;
7496 let out = std::process::Command::new("cargo")
7497 .args(["build", "--release", "--quiet"])
7498 .current_dir(&proj)
7499 .env("CARGO_TARGET_DIR", proj.join("target"))
7500 .env("RUST_MIN_STACK", "268435456")
7501 .output()
7502 .map_err(|e| e.to_string())?;
7503 if !out.status.success() {
7504 return Err(format!(
7505 "native PE build failed:\n{}",
7506 String::from_utf8_lossy(&out.stderr)
7507 ));
7508 }
7509 if bin.exists() {
7510 NATIVE_PE_BIN.with(|c| *c.borrow_mut() = Some(bin.clone()));
7511 Ok(bin)
7512 } else {
7513 Err("native PE binary missing after a successful build".into())
7514 }
7515}
7516
7517#[cfg(not(target_arch = "wasm32"))]
7527struct PeServer {
7528 child: std::process::Child,
7529 stdin: std::process::ChildStdin,
7530 stdout: std::io::BufReader<std::process::ChildStdout>,
7531}
7532
7533#[cfg(not(target_arch = "wasm32"))]
7534impl Drop for PeServer {
7535 fn drop(&mut self) {
7536 let _ = self.child.kill();
7538 let _ = self.child.wait();
7539 }
7540}
7541
7542#[cfg(not(target_arch = "wasm32"))]
7543thread_local! {
7544 static PE_SERVER: std::cell::RefCell<Option<PeServer>> = const { std::cell::RefCell::new(None) };
7545}
7546
7547#[cfg(not(target_arch = "wasm32"))]
7548pub fn run_native_pe_server(program: &str) -> Result<String, String> {
7549 use std::io::{Read, Write};
7550 let bytes = program_to_core_wire_bytes(program)?;
7551 let raw = PE_SERVER.with(|cell| -> Result<String, String> {
7552 let attempt = |slot: &mut Option<PeServer>| -> Result<String, String> {
7555 if slot.is_none() {
7556 let bin = build_native_pe_binary()?;
7557 let mut child = std::process::Command::new(&bin)
7558 .stdin(std::process::Stdio::piped())
7559 .stdout(std::process::Stdio::piped())
7560 .stderr(std::process::Stdio::null())
7561 .env("RUST_MIN_STACK", "268435456")
7562 .spawn()
7563 .map_err(|e| format!("native PE server spawn: {e}"))?;
7564 let stdin = child.stdin.take().unwrap();
7565 let stdout = std::io::BufReader::new(child.stdout.take().unwrap());
7566 *slot = Some(PeServer { child, stdin, stdout });
7567 }
7568 let srv = slot.as_mut().unwrap();
7569 srv.stdin
7570 .write_all(&(bytes.len() as u32).to_le_bytes())
7571 .and_then(|_| srv.stdin.write_all(&bytes))
7572 .and_then(|_| srv.stdin.flush())
7573 .map_err(|e| format!("native PE server write: {e}"))?;
7574 let mut len = [0u8; 4];
7575 srv.stdout
7576 .read_exact(&mut len)
7577 .map_err(|e| format!("native PE server read length: {e}"))?;
7578 let n = u32::from_le_bytes(len) as usize;
7579 let mut buf = vec![0u8; n];
7580 srv.stdout
7581 .read_exact(&mut buf)
7582 .map_err(|e| format!("native PE server read residual: {e}"))?;
7583 String::from_utf8(buf).map_err(|e| format!("native PE residual not UTF-8: {e}"))
7584 };
7585 let mut slot = cell.borrow_mut();
7586 match attempt(&mut slot) {
7587 Ok(v) => Ok(v),
7588 Err(e) => {
7589 *slot = None; Err(e)
7591 }
7592 }
7593 })?;
7594 let residual = finish_projection1_residual(raw)?;
7595 Ok(prepend_type_definitions(program, residual))
7596}
7597
7598#[cfg(not(target_arch = "wasm32"))]
7599thread_local! {
7600 static NATIVE_PE_CDYLIB: std::cell::RefCell<Option<std::path::PathBuf>> = const { std::cell::RefCell::new(None) };
7602}
7603
7604#[cfg(not(target_arch = "wasm32"))]
7610const PE_CDYLIB_SHIM: &str = r#"
7611
7612// ── in-process FFI entry (appended by build_native_pe_cdylib) ───────────────────────────────
7613#[no_mangle]
7614pub extern "C" fn logos_pe_specialize(ptr: *const u8, len: usize, out_len: *mut usize) -> *mut u8 {
7615 let bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
7616 let prog = <CProgram as logicaffeine_data::wire::WireDecode>::wire_decode(bytes, &mut 0usize)
7617 .expect("logos_pe_specialize: decode CProgram");
7618 let out: String = peSpecializeOnce(prog);
7619 let boxed: Box<[u8]> = out.into_bytes().into_boxed_slice();
7620 let n = boxed.len();
7621 unsafe { *out_len = n; }
7622 Box::into_raw(boxed) as *mut u8
7623}
7624
7625#[no_mangle]
7626pub extern "C" fn logos_pe_free(ptr: *mut u8, len: usize) {
7627 if !ptr.is_null() {
7628 unsafe { drop(Box::from_raw(std::slice::from_raw_parts_mut(ptr, len) as *mut [u8])); }
7629 }
7630}
7631"#;
7632
7633#[cfg(not(target_arch = "wasm32"))]
7640pub fn build_native_pe_cdylib() -> Result<std::path::PathBuf, String> {
7641 if let Some(p) = NATIVE_PE_CDYLIB.with(|c| c.borrow().clone()) {
7642 if p.exists() {
7643 return Ok(p);
7644 }
7645 }
7646 let src = pe_cdylib_source();
7647 let key = aot_cache_key(&format!("{src}\n{PE_CDYLIB_SHIM}"));
7649 let base = std::env::temp_dir().join("logos_native_pe_cdylib");
7650 std::fs::create_dir_all(&base).map_err(|e| e.to_string())?;
7651 let proj = base.join(format!("pe_{key}"));
7652 let so = proj.join("target").join("release").join(format!(
7653 "{}logos_native_pe_lib{}",
7654 std::env::consts::DLL_PREFIX,
7655 std::env::consts::DLL_SUFFIX
7656 ));
7657 if so.exists() {
7658 NATIVE_PE_CDYLIB.with(|c| *c.borrow_mut() = Some(so.clone()));
7659 return Ok(so);
7660 }
7661 let generated = compile_program_full_with_wire(&src)
7662 .map_err(|e| format!("native PE cdylib compile: {:?}", e))?
7663 .rust_code;
7664 let lib_rs = format!("{generated}\n{PE_CDYLIB_SHIM}");
7665 std::fs::create_dir_all(proj.join("src")).map_err(|e| e.to_string())?;
7666 let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
7667 let root = manifest.parent().unwrap().parent().unwrap();
7668 std::fs::write(
7669 proj.join("Cargo.toml"),
7670 format!(
7671 "[package]\nname=\"logos_native_pe_lib\"\nversion=\"0.1.0\"\nedition=\"2021\"\n[lib]\ncrate-type=[\"cdylib\"]\npath=\"src/lib.rs\"\n[dependencies]\nlogicaffeine-data={{path=\"{r}/crates/logicaffeine_data\"}}\nlogicaffeine-system={{path=\"{r}/crates/logicaffeine_system\",features=[\"full\"]}}\ntokio={{version=\"1\",features=[\"rt-multi-thread\",\"macros\"]}}\nserde={{version=\"1\",features=[\"derive\"]}}\nrayon=\"1\"\n[profile.release]\nopt-level=3\n",
7672 r = root.display()
7673 ),
7674 )
7675 .map_err(|e| e.to_string())?;
7676 std::fs::write(proj.join("src/lib.rs"), &lib_rs).map_err(|e| e.to_string())?;
7677 std::fs::copy(root.join("Cargo.lock"), proj.join("Cargo.lock")).map_err(|e| e.to_string())?;
7678 let out = std::process::Command::new("cargo")
7679 .args(["build", "--release", "--quiet"])
7680 .current_dir(&proj)
7681 .env("CARGO_TARGET_DIR", proj.join("target"))
7682 .env("RUST_MIN_STACK", "268435456")
7683 .output()
7684 .map_err(|e| e.to_string())?;
7685 if !out.status.success() {
7686 return Err(format!(
7687 "native PE cdylib build failed:\n{}",
7688 String::from_utf8_lossy(&out.stderr)
7689 ));
7690 }
7691 if so.exists() {
7692 NATIVE_PE_CDYLIB.with(|c| *c.borrow_mut() = Some(so.clone()));
7693 Ok(so)
7694 } else {
7695 Err("native PE cdylib missing after a successful build".into())
7696 }
7697}
7698
7699#[cfg(not(target_arch = "wasm32"))]
7703struct PeLib {
7704 _lib: libloading::Library,
7705 specialize: unsafe extern "C" fn(*const u8, usize, *mut usize) -> *mut u8,
7706 free: unsafe extern "C" fn(*mut u8, usize),
7707}
7708
7709#[cfg(not(target_arch = "wasm32"))]
7710thread_local! {
7711 static PE_LIB: std::cell::RefCell<Option<PeLib>> = const { std::cell::RefCell::new(None) };
7712}
7713
7714#[cfg(not(target_arch = "wasm32"))]
7721pub fn run_native_pe_inprocess(program: &str) -> Result<String, String> {
7722 let bytes = program_to_core_wire_bytes(program)?;
7723 let raw = PE_LIB.with(|cell| -> Result<String, String> {
7724 let mut slot = cell.borrow_mut();
7725 if slot.is_none() {
7726 let so = build_native_pe_cdylib()?;
7727 unsafe {
7731 let lib = libloading::Library::new(&so)
7732 .map_err(|e| format!("native PE cdylib load: {e}"))?;
7733 let specialize: libloading::Symbol<
7734 unsafe extern "C" fn(*const u8, usize, *mut usize) -> *mut u8,
7735 > = lib
7736 .get(b"logos_pe_specialize")
7737 .map_err(|e| format!("dlsym logos_pe_specialize: {e}"))?;
7738 let free: libloading::Symbol<unsafe extern "C" fn(*mut u8, usize)> = lib
7739 .get(b"logos_pe_free")
7740 .map_err(|e| format!("dlsym logos_pe_free: {e}"))?;
7741 let specialize = *specialize;
7742 let free = *free;
7743 *slot = Some(PeLib { _lib: lib, specialize, free });
7744 }
7745 }
7746 let pl = slot.as_ref().unwrap();
7747 let mut out_len: usize = 0;
7748 let p = unsafe { (pl.specialize)(bytes.as_ptr(), bytes.len(), &mut out_len as *mut usize) };
7751 if p.is_null() {
7752 return Err("native PE cdylib returned a null residual".into());
7753 }
7754 let residual_bytes = unsafe { std::slice::from_raw_parts(p, out_len).to_vec() };
7755 unsafe { (pl.free)(p, out_len) };
7756 String::from_utf8(residual_bytes).map_err(|e| format!("native PE residual not UTF-8: {e}"))
7757 })?;
7758 let residual = finish_projection1_residual(raw)?;
7759 Ok(prepend_type_definitions(program, residual))
7760}
7761
7762#[cfg(not(target_arch = "wasm32"))]
7768pub fn run_native_pe(program: &str) -> Result<String, String> {
7769 match run_native_pe_inprocess(program) {
7770 Ok(v) => Ok(v),
7771 Err(_) => run_native_pe_server(program),
7772 }
7773}
7774
7775pub fn projection1_source_real_fast_on_vm(
7783 core_types: &str,
7784 _interpreter: &str,
7785 program: &str,
7786) -> Result<String, String> {
7787 let combined = pe_combined_source(core_types, program)?;
7788
7789 let raw_residual = vm_run_source(&combined).map_err(|e| format!("PE VM execution failed: {e}"))?;
7790 let residual = finish_projection1_residual(raw_residual)?;
7791 Ok(prepend_type_definitions(program, residual))
7792}
7793
7794fn prepend_type_definitions(program: &str, residual: String) -> String {
7798 let mut defs = String::new();
7799 let mut keep = false;
7800 for line in program.lines() {
7801 if let Some(header) = line.strip_prefix("## ") {
7802 keep = header != "Main" && !header.starts_with("To ");
7803 }
7804 if keep {
7805 defs.push_str(line);
7806 defs.push('\n');
7807 }
7808 }
7809 if defs.trim().is_empty() {
7810 residual
7811 } else {
7812 format!("{}{}", defs, residual)
7813 }
7814}
7815
7816pub fn pe_combined_source(core_types: &str, program: &str) -> Result<String, String> {
7820 let full_source = if program.contains("## Main") || program.contains("## To ") {
7821 program.to_string()
7822 } else {
7823 format!("## Main\n{}", program)
7824 };
7825
7826 let encoded = encode_program_source(&full_source)
7827 .map_err(|e| format!("Failed to encode program: {:?}", e))?;
7828
7829 let pe_source = pe_source_text();
7830 let decompile_source = decompile_source_text();
7831
7832 let actual_core_types = if core_types.is_empty() { CORE_TYPES_FOR_PE } else { core_types };
7833
7834 let driver = r#"
7838 Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
7839 Let residual be peBlock(encodedMain, state).
7840 Let nl be chr(10).
7841 Let mutable output be "".
7842 Let specFuncs be peFuncs(state).
7843 Let mutable allNames be collectCallNames(residual).
7844 Let mutable emitted be a new Map of Text to Bool.
7845 Let mutable changed be true.
7846 While changed:
7847 Set changed to false.
7848 Let mutable toAdd be a new Seq of Text.
7849 Repeat for fnKey in allNames:
7850 Let fkStr be "{fnKey}".
7851 If emitted contains fkStr:
7852 Let skipE be true.
7853 Otherwise:
7854 Set item fkStr of emitted to true.
7855 Let fkStr2 be "{fnKey}".
7856 If specFuncs contains fkStr2:
7857 Let fdef be item fkStr2 of specFuncs.
7858 Inspect fdef:
7859 When CFuncDef (fn0, ps0, pt0, rt0, body0):
7860 Let children be collectCallNames(body0).
7861 Repeat for child in children:
7862 Let childStr be "{child}".
7863 If not emitted contains childStr:
7864 Push child to toAdd.
7865 Set changed to true.
7866 Otherwise:
7867 Let skipF be true.
7868 Repeat for ta in toAdd:
7869 Push ta to allNames.
7870 Repeat for fnKey in allNames:
7871 Let fkStr be "{fnKey}".
7872 If specFuncs contains fkStr:
7873 Let fdef be item fkStr of specFuncs.
7874 Let funcSrc be decompileFunc(fdef).
7875 If the length of funcSrc is greater than 0:
7876 Set output to "{output}{funcSrc}{nl}".
7877 Let mainSrc be decompileBlock(residual, 0).
7878 Set output to "{output}## Main{nl}{mainSrc}".
7879 Show output.
7880"#;
7881
7882 let combined = format!(
7883 "{}\n{}\n{}\n## Main\n{}\n{}",
7884 actual_core_types,
7885 pe_source,
7886 decompile_source,
7887 encoded,
7888 driver,
7889 );
7890
7891 Ok(combined)
7892}
7893
7894pub fn finish_projection1_residual(raw_residual: String) -> Result<String, String> {
7897 let fixed = raw_residual
7902 .replace(": Any)", ": Int)")
7903 .replace("-> Any:", "-> Int:")
7904 .replace(" of Any", " of Int")
7905 .replace(" to Any", " to Int");
7906 let trimmed = fixed.trim();
7907
7908 if trimmed.is_empty() {
7909 return Ok("## Main\n".to_string());
7910 }
7911
7912 if trimmed.contains("## To ") || trimmed.starts_with("## Main") {
7913 Ok(trimmed.to_string())
7914 } else {
7915 Ok(format!("## Main\n{}", trimmed))
7916 }
7917}
7918
7919pub fn run_genuine_p2_on_target(program: &str, core_types: &str, interpreter: &str) -> Result<String, String> {
7931 let pe_mini = pe_mini_source_text();
7932 let pe = pe_source_text();
7933
7934 let full_source = if program.contains("## Main") || program.contains("## To ") {
7935 program.to_string()
7936 } else {
7937 format!("## Main\n{}", program)
7938 };
7939
7940 let target_encoded = encode_program_source(&full_source)
7945 .map_err(|e| format!("Failed to encode target: {:?}", e))?;
7946 let pe_mini_prog = format!(
7947 "{}\n{}\n{}\n## Main\n{}\n\
7948 Let compileEnv be a new Map of Text to CVal.\n\
7949 Let compileState be makePeState(compileEnv, encodedFuncMap, 200).\n\
7950 Let compiled be peBlockM(encodedMain, compileState).\n\
7951 Let runEnv be a new Map of Text to CVal.\n\
7952 coreExecBlock(compiled, runEnv, encodedFuncMap).\n",
7953 core_types, pe_mini, interpreter, target_encoded
7954 );
7955
7956 let encoded = encode_program_source_compact(&pe_mini_prog)
7958 .map_err(|e| format!("Failed to encode pe_mini+target for P2: {:?}", e))?;
7959
7960 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 500).
7965 Let residual be peBlock(encodedMain, state).
7966 Let allFuncs be peFuncs(state).
7967 Let runEnv be a new Map of Text to CVal.
7968 coreExecBlock(residual, runEnv, allFuncs).
7969"#;
7970 let combined = format!(
7971 "{}\n{}\n{}\n## Main\n{}\n{}",
7972 CORE_TYPES_FOR_PE, pe, interpreter, encoded, driver
7973 );
7974
7975 run_logos_source(&combined)
7976}
7977
7978pub fn run_genuine_p3_on_target(program: &str, core_types: &str, interpreter: &str) -> Result<String, String> {
7980 let pe_bti = pe_bti_source_text();
7981 let pe = pe_source_text();
7982
7983 let full_source = if program.contains("## Main") || program.contains("## To ") {
7984 program.to_string()
7985 } else {
7986 format!("## Main\n{}", program)
7987 };
7988
7989 let bti_types = CORE_TYPES_FOR_PE
7990 .replace("specResults", "memoCache")
7991 .replace("onStack", "callGuard");
7992
7993 let target_encoded = encode_program_source(&full_source)
7994 .map_err(|e| format!("Failed to encode target: {:?}", e))?;
7995 let pe_bti_prog = format!(
7996 "{}\n{}\n{}\n## Main\n{}\n\
7997 Let compileEnv be a new Map of Text to CVal.\n\
7998 Let compileState be makePeState(compileEnv, encodedFuncMap, 200).\n\
7999 Let compiled be peBlockB(encodedMain, compileState).\n\
8000 Let runEnv be a new Map of Text to CVal.\n\
8001 coreExecBlock(compiled, runEnv, encodedFuncMap).\n",
8002 bti_types, pe_bti, interpreter, target_encoded
8003 );
8004
8005 let encoded = encode_program_source_compact(&pe_bti_prog)
8006 .map_err(|e| format!("Failed to encode pe_bti+target for P3: {:?}", e))?;
8007
8008 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
8010 Let residual be peBlock(encodedMain, state).
8011 Let runEnv be a new Map of Text to CVal.
8012 coreExecBlock(residual, runEnv, encodedFuncMap).
8013"#;
8014 let combined = format!(
8015 "{}\n{}\n{}\n## Main\n{}\n{}",
8016 CORE_TYPES_FOR_PE, pe, interpreter, encoded, driver
8017 );
8018
8019 run_logos_source(&combined)
8020}
8021
8022pub fn projection2_source_real(_core_types: &str, _interpreter: &str) -> Result<GenuineProjectionResult, String> {
8032 let pe_mini = pe_mini_source_text();
8033 let pe = pe_source_text();
8034 let decompile = decompile_source_text();
8035
8036 let program = format!(
8046 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockM(targetStmts, state).\n Show \"done\".\n",
8047 CORE_TYPES_FOR_PE, pe_mini
8048 );
8049
8050 let encoded = encode_program_source_compact(&program)
8052 .map_err(|e| format!("Failed to encode pe_mini for P2: {:?}", e))?;
8053
8054 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
8058 Let residual be peBlock(encodedMain, state).
8059 Let nl be chr(10).
8060 Let mutable output be "".
8061 Let specFuncs be peFuncs(state).
8062 Let mutable allNames be collectCallNames(residual).
8063 Let mutable emitted be a new Map of Text to Bool.
8064 Let mutable changed be true.
8065 While changed:
8066 Set changed to false.
8067 Let mutable toAdd be a new Seq of Text.
8068 Repeat for fnKey in allNames:
8069 Let fkStr be "{fnKey}".
8070 If emitted contains fkStr:
8071 Let skipE be true.
8072 Otherwise:
8073 Set item fkStr of emitted to true.
8074 Let fkStr2 be "{fnKey}".
8075 If specFuncs contains fkStr2:
8076 Let fdef be item fkStr2 of specFuncs.
8077 Inspect fdef:
8078 When CFuncDef (fn0, ps0, pt0, rt0, body0):
8079 Let children be collectCallNames(body0).
8080 Repeat for child in children:
8081 Let childStr be "{child}".
8082 If not emitted contains childStr:
8083 Push child to toAdd.
8084 Set changed to true.
8085 Otherwise:
8086 Let skipF be true.
8087 Repeat for ta in toAdd:
8088 Push ta to allNames.
8089 Repeat for fnKey in allNames:
8090 Let fkStr be "{fnKey}".
8091 If specFuncs contains fkStr:
8092 Let fdef be item fkStr of specFuncs.
8093 Let funcSrc be decompileFunc(fdef).
8094 If the length of funcSrc is greater than 0:
8095 Set output to "{output}{funcSrc}{nl}".
8096 Let mainSrc be decompileBlock(residual, 0).
8097 Set output to "{output}## Main{nl}{mainSrc}".
8098 Show output.
8099"#;
8100 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
8101
8102 let result = run_logos_source(&combined)?;
8103
8104 let result = fix_decompiled_types(&result, &[
8109 ("peExprM_", "(e: CExpr) -> CExpr:"),
8110 ("peBlockM_", "(stmts: Seq of CStmt) -> Seq of CStmt:"),
8111 ("checkLiteralM_", "(e: CExpr) -> Bool:"),
8112 ("exprToValM_", "(e: CExpr) -> CVal:"),
8113 ("valToExprM_", "(v: CVal) -> CExpr:"),
8114 ("evalBinOpM_", "(binOp: Text) and (lv: CVal) and (rv: CVal) -> CVal:"),
8115 ("isCopyPropSafeM_", "(e: CExpr) -> Bool:"),
8116 ("checkVNothingM_", "(v: CVal) -> Bool:"),
8117 ("hasReturnM_", "(stmts: Seq of CStmt) -> Bool:"),
8118 ("extractReturnM_", "(stmts: Seq of CStmt) -> CExpr:"),
8119 ("validateExtractReturnM_", "(result: CExpr) and (bodyStmts: Seq of CStmt) -> CExpr:"),
8120 ("makeKeyM_", "(fnName: Text) and (args: Seq of CExpr) -> Text:"),
8121 ("exprToKeyPartM_", "(e: CExpr) -> Text:"),
8122 ("collectSetVarsM_", "(stmts: Seq of CStmt) -> Seq of Text:"),
8123 ("peEnvM_", "(st: PEMiniState) -> Map of Text to CVal:"),
8124 ("peFuncsM_", "(st: PEMiniState) -> Map of Text to CFunc:"),
8125 ("peDepthM_", "(st: PEMiniState) -> Int:"),
8126 ("peStaticEnvM_", "(st: PEMiniState) -> Map of Text to CExpr:"),
8127 ("peMemoCacheM_", "(st: PEMiniState) -> Map of Text to CExpr:"),
8128 ("peStateWithEnvDepthM_", "(st: PEMiniState) and (newEnv: Map of Text to CVal) and (d: Int) -> PEMiniState:"),
8129 ("peStateWithEnvDepthStaticM_", "(st: PEMiniState) and (newEnv: Map of Text to CVal) and (d: Int) and (newSe: Map of Text to CExpr) -> PEMiniState:"),
8130 ]);
8131
8132 let (block_entry, expr_entry) = discover_entry_points(&result, "peBlockM_", "peExprM_");
8134 if block_entry.is_empty() {
8135 return Err("Genuine P2: no peBlockM_ entry found in residual".to_string());
8136 }
8137
8138 let func_defs_only = strip_main_block(&result);
8141
8142 let pe_mini_helpers = pe_mini_source_text();
8146
8147 let alias = format!(
8151 "\n## To compileBlock (stmts: Seq of CStmt) -> Seq of CStmt:\n Return {}(stmts).\n",
8152 block_entry
8153 );
8154
8155 let combined = format!("{}\n{}\n{}", pe_mini_helpers, func_defs_only, alias);
8159 let full_source = deduplicate_functions(&combined);
8160
8161 Ok(GenuineProjectionResult {
8162 source: full_source,
8163 block_entry: "compileBlock".to_string(),
8164 expr_entry,
8165 })
8166}
8167
8168pub fn genuine_projection2_residual() -> Result<String, String> {
8179 let pe_mini = pe_mini_source_text();
8180 let pe = pe_source_text();
8181 let decompile = decompile_source_text();
8182
8183 let program = format!(
8185 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockM(targetStmts, state).\n Show \"done\".\n",
8186 CORE_TYPES_FOR_PE, pe_mini
8187 );
8188
8189 let encoded = encode_program_source_compact(&program)
8191 .map_err(|e| format!("Failed to encode pe_mini: {:?}", e))?;
8192
8193 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
8195 Let residual be peBlock(encodedMain, state).
8196 Let nl be chr(10).
8197 Let mutable output be "".
8198 Let specFuncs be peFuncs(state).
8199 Let specNames be collectCallNames(residual).
8200 Repeat for sn in specNames:
8201 Let snKey be "{sn}".
8202 If specFuncs contains snKey:
8203 Let fdef be item snKey of specFuncs.
8204 Let funcSrc be decompileFunc(fdef).
8205 If the length of funcSrc is greater than 0:
8206 Set output to "{output}{funcSrc}{nl}".
8207 Let mainSrc be decompileBlock(residual, 0).
8208 Set output to "{output}## Main{nl}{mainSrc}".
8209 Show output.
8210"#;
8211 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
8212
8213 let result = run_logos_source(&combined)?;
8214 Ok(result)
8215}
8216
8217pub fn genuine_projection3_residual() -> Result<String, String> {
8228 let pe_bti = pe_bti_source_text();
8229 let pe = pe_source_text();
8230 let decompile = decompile_source_text();
8231
8232 let bti_types = CORE_TYPES_FOR_PE
8234 .replace("specResults", "memoCache")
8235 .replace("onStack", "callGuard");
8236
8237 let program = format!(
8239 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockB(targetStmts, state).\n Show \"done\".\n",
8240 bti_types, pe_bti
8241 );
8242
8243 let encoded = encode_program_source_compact(&program)
8245 .map_err(|e| format!("Failed to encode pe_bti: {:?}", e))?;
8246
8247 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
8249 Let residual be peBlock(encodedMain, state).
8250 Let nl be chr(10).
8251 Let mutable output be "".
8252 Let specFuncs be peFuncs(state).
8253 Let specNames be collectCallNames(residual).
8254 Repeat for sn in specNames:
8255 Let snKey be "{sn}".
8256 If specFuncs contains snKey:
8257 Let fdef be item snKey of specFuncs.
8258 Let funcSrc be decompileFunc(fdef).
8259 If the length of funcSrc is greater than 0:
8260 Set output to "{output}{funcSrc}{nl}".
8261 Let mainSrc be decompileBlock(residual, 0).
8262 Set output to "{output}## Main{nl}{mainSrc}".
8263 Show output.
8264"#;
8265 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
8266
8267 let result = run_logos_source(&combined)?;
8268 Ok(result)
8269}
8270
8271pub fn projection3_source_real(_core_types: &str) -> Result<GenuineProjectionResult, String> {
8280 let pe_bti = pe_bti_source_text();
8281 let pe = pe_source_text();
8282 let decompile = decompile_source_text();
8283
8284 let bti_types = CORE_TYPES_FOR_PE
8286 .replace("specResults", "memoCache")
8287 .replace("onStack", "callGuard");
8288
8289 let program = format!(
8291 "{}\n{}\n## Main\n Let env be a new Map of Text to CVal.\n Let funcs be a new Map of Text to CFunc.\n Let state be makePeState(env, funcs, 200).\n Let result be peBlockB(targetStmts, state).\n Show \"done\".\n",
8292 bti_types, pe_bti
8293 );
8294
8295 let encoded = encode_program_source_compact(&program)
8297 .map_err(|e| format!("Failed to encode pe_bti for P3: {:?}", e))?;
8298
8299 let driver = r#" Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
8301 Let residual be peBlock(encodedMain, state).
8302 Let nl be chr(10).
8303 Let mutable output be "".
8304 Let specFuncs be peFuncs(state).
8305 Let mutable allNames be collectCallNames(residual).
8306 Let mutable emitted be a new Map of Text to Bool.
8307 Let mutable changed be true.
8308 While changed:
8309 Set changed to false.
8310 Let mutable toAdd be a new Seq of Text.
8311 Repeat for fnKey in allNames:
8312 Let fkStr be "{fnKey}".
8313 If emitted contains fkStr:
8314 Let skipE be true.
8315 Otherwise:
8316 Set item fkStr of emitted to true.
8317 Let fkStr2 be "{fnKey}".
8318 If specFuncs contains fkStr2:
8319 Let fdef be item fkStr2 of specFuncs.
8320 Inspect fdef:
8321 When CFuncDef (fn0, ps0, pt0, rt0, body0):
8322 Let children be collectCallNames(body0).
8323 Repeat for child in children:
8324 Let childStr be "{child}".
8325 If not emitted contains childStr:
8326 Push child to toAdd.
8327 Set changed to true.
8328 Otherwise:
8329 Let skipF be true.
8330 Repeat for ta in toAdd:
8331 Push ta to allNames.
8332 Repeat for fnKey in allNames:
8333 Let fkStr be "{fnKey}".
8334 If specFuncs contains fkStr:
8335 Let fdef be item fkStr of specFuncs.
8336 Let funcSrc be decompileFunc(fdef).
8337 If the length of funcSrc is greater than 0:
8338 Set output to "{output}{funcSrc}{nl}".
8339 Let mainSrc be decompileBlock(residual, 0).
8340 Set output to "{output}## Main{nl}{mainSrc}".
8341 Show output.
8342"#;
8343 let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
8344
8345 let result = run_logos_source(&combined)?;
8346
8347 let result = fix_decompiled_types(&result, &[
8350 ("peExprB_", "(e: CExpr) -> CExpr:"),
8351 ("peBlockB_", "(stmts: Seq of CStmt) -> Seq of CStmt:"),
8352 ("isStatic_", "(e: CExpr) -> Bool:"),
8353 ("isLiteral_", "(e: CExpr) -> Bool:"),
8354 ("allStatic_", "(args: Seq of CExpr) -> Bool:"),
8355 ("exprToVal_", "(e: CExpr) -> CVal:"),
8356 ("valToExpr_", "(v: CVal) -> CExpr:"),
8357 ("evalBinOp_", "(binOp: Text) and (lv: CVal) and (rv: CVal) -> CVal:"),
8358 ("isCopyPropSafe_", "(e: CExpr) -> Bool:"),
8359 ("isVNothing_", "(v: CVal) -> Bool:"),
8360 ("hasReturn_", "(stmts: Seq of CStmt) -> Bool:"),
8361 ("extractReturnB_", "(stmts: Seq of CStmt) -> CExpr:"),
8362 ("makeKey_", "(fnName: Text) and (args: Seq of CExpr) -> Text:"),
8363 ("exprToKeyPartB_", "(e: CExpr) -> Text:"),
8364 ("collectSetVars_", "(stmts: Seq of CStmt) -> Seq of Text:"),
8365 ]);
8366
8367 let (block_entry, expr_entry) = discover_entry_points(&result, "peBlockB_", "peExprB_");
8369 if block_entry.is_empty() {
8370 return Err("Genuine P3: no peBlockB_ entry found in residual".to_string());
8371 }
8372
8373 let func_defs_only = strip_main_block(&result);
8375
8376 let pe_bti_helpers = pe_bti_source_text();
8378
8379 let alias = format!(
8382 "\n## To cogenBlock (stmts: Seq of CStmt) -> Seq of CStmt:\n Return {}(stmts).\n",
8383 block_entry
8384 );
8385 let combined = format!("{}\n{}\n{}", pe_bti_helpers, func_defs_only, alias);
8386 let full_source = deduplicate_functions(&combined);
8387
8388 Ok(GenuineProjectionResult {
8389 source: full_source,
8390 block_entry: "cogenBlock".to_string(),
8391 expr_entry,
8392 })
8393}
8394
8395fn deduplicate_functions(source: &str) -> String {
8397 let mut seen = std::collections::HashSet::new();
8398 let mut result = String::with_capacity(source.len());
8399 let mut skip_until_next = false;
8400 for line in source.lines() {
8401 let trimmed = line.trim();
8402 if let Some(rest) = trimmed.strip_prefix("## To ") {
8403 let name = rest.split(' ').next().unwrap_or("");
8404 if !seen.insert(name.to_string()) {
8405 skip_until_next = true;
8406 continue;
8407 }
8408 skip_until_next = false;
8409 } else if trimmed.starts_with("## Main") {
8410 skip_until_next = false;
8411 } else if skip_until_next {
8412 if !trimmed.starts_with("## ") {
8414 continue;
8415 }
8416 skip_until_next = false;
8417 }
8418 result.push_str(line);
8419 result.push('\n');
8420 }
8421 result
8422}
8423
8424fn strip_main_block(source: &str) -> String {
8428 let mut result = String::with_capacity(source.len());
8429 let mut in_main = false;
8430 for line in source.lines() {
8431 let trimmed = line.trim();
8432 if trimmed == "## Main" {
8433 in_main = true;
8434 continue;
8435 }
8436 if in_main {
8437 if trimmed.starts_with("## To ") {
8439 in_main = false;
8440 } else {
8441 continue;
8442 }
8443 }
8444 result.push_str(line);
8445 result.push('\n');
8446 }
8447 result
8448}
8449
8450fn extract_main_block(source: &str) -> String {
8452 let mut result = String::new();
8453 let mut in_main = false;
8454 for line in source.lines() {
8455 let trimmed = line.trim();
8456 if trimmed == "## Main" {
8457 in_main = true;
8458 continue;
8459 }
8460 if in_main {
8461 if trimmed.starts_with("## To ") {
8462 break;
8463 }
8464 result.push_str(line);
8465 result.push('\n');
8466 }
8467 }
8468 result
8469}
8470
8471fn fix_decompiled_types(source: &str, type_map: &[(&str, &str)]) -> String {
8475 let mut result = String::with_capacity(source.len());
8476 for line in source.lines() {
8477 let trimmed = line.trim();
8478 if let Some(rest) = trimmed.strip_prefix("## To ") {
8479 let name = rest.split(' ').next().unwrap_or("");
8480 let mut fixed = false;
8481 for (prefix, sig) in type_map {
8482 if name.starts_with(prefix) {
8483 result.push_str(&format!("## To {} {}\n", name, sig));
8484 fixed = true;
8485 break;
8486 }
8487 }
8488 if !fixed {
8489 result.push_str(line);
8490 result.push('\n');
8491 }
8492 } else {
8493 result.push_str(line);
8494 result.push('\n');
8495 }
8496 }
8497 let result = result
8498 .replace("Seq of Any", "Seq of CExpr")
8499 .replace("Set of Any", "Set of CExpr")
8500 .replace(": Any)", ": CExpr)")
8501 .replace("-> Any:", "-> CExpr:");
8502 result
8503}
8504
8505fn replace_word(source: &str, from: &str, to: &str) -> String {
8506 let mut result = String::with_capacity(source.len());
8507 let mut remaining = source;
8508 while let Some(pos) = remaining.find(from) {
8509 let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
8510 let after_pos = pos + from.len();
8511 let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
8512 let is_word = !before.is_ascii_alphanumeric() && before != b'_'
8513 && !after.is_ascii_alphanumeric() && after != b'_';
8514 result.push_str(&remaining[..pos]);
8515 if is_word {
8516 result.push_str(to);
8517 } else {
8518 result.push_str(from);
8519 }
8520 remaining = &remaining[after_pos..];
8521 }
8522 result.push_str(remaining);
8523 result
8524}
8525
8526#[cfg(test)]
8527mod tests {
8528 use super::*;
8529
8530 #[test]
8531 fn test_compile_let_statement() {
8532 let source = "## Main\nLet x be 5.";
8533 let result = compile_to_rust(source);
8534 assert!(result.is_ok(), "Should compile: {:?}", result);
8535 let rust = result.unwrap();
8536 assert!(rust.contains("fn main()"));
8537 assert!(rust.contains("let x = 5;"));
8538 }
8539
8540 #[test]
8541 fn test_compile_return_statement() {
8542 let source = "## Main\nReturn 42.";
8543 let result = compile_to_rust(source);
8544 assert!(result.is_ok(), "Should compile: {:?}", result);
8545 let rust = result.unwrap();
8546 assert!(rust.contains("return 42;"));
8547 }
8548
8549 fn optimized_vm_outcome_no_tier(src: &str, argv: &[String]) -> (String, Option<String>) {
8553 crate::ui_bridge::with_optimized_program(src, |parsed, interner| match parsed {
8554 Ok((stmts, types, policies)) => crate::vm::run_to_outcome_with_args(
8555 stmts,
8556 interner,
8557 Some(types),
8558 Some(&policies),
8559 argv,
8560 None,
8561 ),
8562 Err(advice) => (String::new(), Some(advice)),
8563 })
8564 }
8565
8566 fn norm_lines(s: &str) -> String {
8567 s.lines()
8568 .map(|l| l.trim_end())
8569 .filter(|l| !l.is_empty())
8570 .collect::<Vec<_>>()
8571 .join("\n")
8572 }
8573
8574 #[test]
8581 fn licm_indexed_load_nbody_shape_matches_oracle_inprocess() {
8582 let src = "## Main\n\
8583 Let arr be [10, 20, 30, 40, 50].\n\
8584 Let mutable acc be 0.\n\
8585 Let mutable i be 1.\n\
8586 While i is at most 5:\n\
8587 \x20 Let mutable j be 1.\n\
8588 \x20 While j is at most 5:\n\
8589 \x20 Let d be item i of arr - item j of arr.\n\
8590 \x20 Set acc to acc + d * d.\n\
8591 \x20 Set j to j + 1.\n\
8592 \x20 Set i to i + 1.\n\
8593 Show acc.\n";
8594 let argv: [String; 0] = [];
8595 let tw = tw_outcome_with_args(src, &argv);
8596 let (out, err) = optimized_vm_outcome_no_tier(src, &argv);
8597 assert_eq!(
8598 (norm_lines(&tw.output), &tw.error),
8599 ("10000".to_string(), &None),
8600 "oracle: sum of (a_i - a_j)^2"
8601 );
8602 assert_eq!(
8603 (norm_lines(&out), &err),
8604 (norm_lines(&tw.output), &tw.error),
8605 "optimized VM (LICM) must match the tree-walker on the nbody-shaped loop"
8606 );
8607 }
8608
8609 #[test]
8615 fn corpus_optimized_vm_matches_treewalker_no_tier() {
8616 const CORPUS: &[(&str, &str)] = &[
8617 ("ackermann", "3"),
8618 ("array_fill", "2000"),
8619 ("array_reverse", "2000"),
8620 ("binary_trees", "6"),
8621 ("bubble_sort", "60"),
8622 ("coins", "500"),
8623 ("collatz", "300"),
8624 ("collect", "300"),
8625 ("counting_sort", "2000"),
8626 ("fannkuch", "5"),
8627 ("fib", "12"),
8628 ("fib_iterative", "500"),
8629 ("gcd", "60"),
8630 ("graph_bfs", "200"),
8631 ("heap_sort", "300"),
8632 ("histogram", "2000"),
8633 ("knapsack", "30"),
8634 ("loop_sum", "2000"),
8635 ("mandelbrot", "20"),
8636 ("matrix_mult", "8"),
8637 ("mergesort", "300"),
8638 ("nbody", "100"),
8639 ("nqueens", "5"),
8640 ("pi_leibniz", "2000"),
8641 ("prefix_sum", "2000"),
8642 ("primes", "500"),
8643 ("quicksort", "300"),
8644 ("sieve", "2000"),
8645 ("spectral_norm", "20"),
8646 ("string_search", "1200"),
8647 ("strings", "200"),
8648 ("two_sum", "300"),
8649 ];
8650 std::thread::Builder::new()
8651 .stack_size(256 * 1024 * 1024)
8652 .spawn(|| {
8653 for &(name, size) in CORPUS {
8654 let path = format!(
8655 "{}/../../benchmarks/programs/{}/main.lg",
8656 env!("CARGO_MANIFEST_DIR"),
8657 name
8658 );
8659 let src = std::fs::read_to_string(&path)
8660 .unwrap_or_else(|e| panic!("cannot read {path}: {e}"));
8661 let argv = vec!["bench".to_string(), size.to_string()];
8662 let (out, err) = optimized_vm_outcome_no_tier(&src, &argv);
8663 let tw = tw_outcome_with_args(&src, &argv);
8664 assert_eq!(
8665 (norm_lines(&out), &err),
8666 (norm_lines(&tw.output), &tw.error),
8667 "OPTIMIZED VM (no tier) diverged from the tree-walker on '{name}' at {size}"
8668 );
8669 }
8670 })
8671 .expect("spawn")
8672 .join()
8673 .expect("corpus thread panicked");
8674 }
8675}