use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use crate::analysis::registry::{FieldDef, TypeDef, TypeRegistry};
use crate::analysis::policy::PolicyRegistry;
use crate::analysis::types::RustNames;
use crate::ast::stmt::{Expr, OptFlag, Stmt, TypeExpr};
use crate::intern::{Interner, Symbol};
use crate::registry::SymbolRegistry;
use super::context::{RefinementContext, VariableCapabilities, analyze_variable_capabilities};
use crate::analysis::callgraph::CallGraph;
use crate::analysis::liveness::LivenessResult;
use crate::analysis::readonly::{ReadonlyParams, MutableBorrowParams};
use super::detection::{
requires_async, requires_vfs, collect_mutable_vars,
collect_crdt_register_fields, collect_boxed_fields, collect_async_functions,
collect_pure_functions, count_self_calls, is_hashable_type, is_copy_type_expr,
should_memoize, body_contains_self_call, should_inline,
collect_pipe_sender_params, collect_pipe_vars,
collect_mutable_vars_stmt, is_result_type,
vec_to_slice_type, vec_to_mut_slice_type, collect_give_arg_indices, is_vec_type_expr,
collect_single_char_text_vars, collect_escaping_collection_vars,
detect_double_recursion_closed_form,
};
use super::expr::{codegen_expr, codegen_expr_with_async};
use super::ffi::{
has_wasm_exports, has_c_exports, has_c_exports_with_text,
codegen_logos_runtime_preamble, collect_c_export_reference_types,
collect_c_export_value_type_structs,
};
use super::marshal::{is_text_type, codegen_c_export_with_marshaling};
use super::policy::codegen_policy_impls;
use super::stmt::codegen_stmt;
use super::tce::{
is_tail_recursive, detect_accumulator_pattern, codegen_stmt_acc,
detect_mutual_tce_pairs, codegen_mutual_tce_pair, codegen_stmt_tce,
};
use super::types::{
codegen_type_expr, infer_return_type_from_body,
codegen_struct_def, codegen_enum_def,
};
use super::{escape_rust_ident, is_rust_keyword};
use super::{
collect_c_export_ref_structs, codegen_c_accessors,
try_emit_vec_fill_pattern, try_emit_for_range_pattern, try_emit_swap_pattern,
try_emit_seq_copy_pattern, try_emit_seq_from_slice_pattern,
try_emit_bare_slice_push_pattern,
try_emit_vec_with_capacity_pattern, try_emit_merge_capacity_pattern,
try_emit_string_with_capacity_pattern,
try_emit_rotate_left_pattern,
try_emit_buffer_reuse_while,
classify_type_for_c_abi, CAbiClass,
};
fn body_contains_escape(body: &[Stmt]) -> bool {
body.iter().any(|stmt| stmt_contains_escape(stmt))
}
fn stmt_contains_escape(stmt: &Stmt) -> bool {
match stmt {
Stmt::Escape { .. } => true,
Stmt::Let { value, .. } => expr_contains_escape(value),
Stmt::Set { value, .. } => expr_contains_escape(value),
Stmt::If { then_block, else_block, .. } => {
body_contains_escape(then_block)
|| else_block.as_ref().map_or(false, |eb| body_contains_escape(eb))
}
Stmt::While { body, .. } | Stmt::Repeat { body, .. } => body_contains_escape(body),
Stmt::Inspect { arms, .. } => arms.iter().any(|arm| body_contains_escape(arm.body)),
_ => false,
}
}
fn expr_contains_escape(expr: &Expr) -> bool {
matches!(expr, Expr::Escape { .. })
}
pub fn codegen_program(stmts: &[Stmt], registry: &TypeRegistry, policies: &PolicyRegistry, interner: &Interner, type_env: &crate::analysis::types::TypeEnv) -> String {
let mut output = String::new();
writeln!(output, "#[allow(unused_imports)]").unwrap();
writeln!(output, "use std::fmt::Write as _;").unwrap();
writeln!(output, "use logicaffeine_data::*;").unwrap();
writeln!(output, "use logicaffeine_system::*;\n").unwrap();
if has_wasm_exports(stmts, interner) {
writeln!(output, "use wasm_bindgen::prelude::*;\n").unwrap();
}
if has_c_exports_with_text(stmts, interner) {
writeln!(output, "use std::ffi::{{CStr, CString}};\n").unwrap();
}
let c_exports_exist = has_c_exports(stmts, interner);
if c_exports_exist {
output.push_str(&codegen_logos_runtime_preamble());
}
let (lww_fields, mv_fields) = collect_crdt_register_fields(registry, interner);
let async_functions = collect_async_functions(stmts);
let pure_functions = collect_pure_functions(stmts);
let main_pipe_vars = collect_pipe_vars(stmts);
let boxed_fields = collect_boxed_fields(registry, interner);
let c_abi_value_structs: HashSet<Symbol> = if c_exports_exist {
collect_c_export_value_type_structs(stmts, interner, registry)
} else {
HashSet::new()
};
let c_abi_ref_structs: HashSet<Symbol> = if c_exports_exist {
collect_c_export_ref_structs(stmts, interner, registry)
} else {
HashSet::new()
};
let structs: Vec<_> = registry.iter_types()
.filter_map(|(name, def)| {
if let TypeDef::Struct { fields, generics, is_portable, is_shared } = def {
if !fields.is_empty() || !generics.is_empty() {
Some((*name, fields.clone(), generics.clone(), *is_portable, *is_shared))
} else {
None
}
} else {
None
}
})
.collect();
let enums: Vec<_> = registry.iter_types()
.filter_map(|(name, def)| {
if let TypeDef::Enum { variants, generics, is_portable, is_shared } = def {
if !variants.is_empty() || !generics.is_empty() {
Some((*name, variants.clone(), generics.clone(), *is_portable, *is_shared))
} else {
None
}
} else {
None
}
})
.collect();
if !structs.is_empty() || !enums.is_empty() {
writeln!(output, "pub mod user_types {{").unwrap();
writeln!(output, " use super::*;\n").unwrap();
for (name, fields, generics, is_portable, is_shared) in &structs {
output.push_str(&codegen_struct_def(*name, fields, generics, *is_portable, *is_shared, interner, 4, &c_abi_value_structs, &c_abi_ref_structs));
}
for (name, variants, generics, is_portable, is_shared) in &enums {
output.push_str(&codegen_enum_def(*name, variants, generics, *is_portable, *is_shared, interner, 4));
}
writeln!(output, "}}\n").unwrap();
writeln!(output, "use user_types::*;\n").unwrap();
}
output.push_str(&codegen_policy_impls(policies, interner));
let mutual_tce_pairs = detect_mutual_tce_pairs(stmts, interner);
let mut mutual_tce_members: HashSet<Symbol> = HashSet::new();
for (a, b) in &mutual_tce_pairs {
mutual_tce_members.insert(*a);
mutual_tce_members.insert(*b);
}
let mut mutual_tce_emitted: HashSet<Symbol> = HashSet::new();
let callgraph = CallGraph::build(stmts, interner);
let readonly_params = ReadonlyParams::analyze(stmts, &callgraph, type_env);
let mut borrow_params_map: HashMap<Symbol, HashSet<usize>> = HashMap::new();
for stmt in stmts {
if let Stmt::FunctionDef { name, params, body, is_native, is_exported, opt_flags, .. } = stmt {
if *is_native || *is_exported || mutual_tce_members.contains(name) {
continue;
}
if opt_flags.contains(&OptFlag::NoBorrow) || opt_flags.contains(&OptFlag::NoOptimize) {
continue;
}
if is_tail_recursive(*name, body) {
continue;
}
if detect_accumulator_pattern(*name, body).is_some() {
continue;
}
if body_contains_escape(body) {
continue;
}
let indices: HashSet<usize> = params.iter().enumerate()
.filter(|(_, (sym, param_type))| {
readonly_params.is_readonly(*name, *sym)
&& is_vec_type_expr(param_type, interner)
})
.map(|(i, _)| i)
.collect();
if !indices.is_empty() {
let give_indices = collect_give_arg_indices(*name, stmts);
let filtered: HashSet<usize> = indices.difference(&give_indices).copied().collect();
if !filtered.is_empty() {
borrow_params_map.insert(*name, filtered);
}
}
}
}
let mutable_borrow_params = MutableBorrowParams::analyze(stmts, &callgraph, type_env);
let mut mut_borrow_params_map: HashMap<Symbol, HashSet<usize>> = HashMap::new();
for stmt in stmts {
if let Stmt::FunctionDef { name, params, body, is_native, is_exported, opt_flags, .. } = stmt {
if *is_native || *is_exported || mutual_tce_members.contains(name) {
continue;
}
if opt_flags.contains(&OptFlag::NoBorrow) || opt_flags.contains(&OptFlag::NoOptimize) {
continue;
}
if is_tail_recursive(*name, body) || detect_accumulator_pattern(*name, body).is_some() {
continue;
}
if body_contains_escape(body) {
continue;
}
let readonly_indices = borrow_params_map.get(name).cloned().unwrap_or_default();
let indices: HashSet<usize> = params.iter().enumerate()
.filter(|(i, (sym, param_type))| {
mutable_borrow_params.is_mutable_borrow(*name, *sym)
&& !readonly_indices.contains(i)
&& is_vec_type_expr(param_type, interner)
})
.map(|(i, _)| i)
.collect();
if !indices.is_empty() {
mut_borrow_params_map.insert(*name, indices);
}
}
}
let liveness = LivenessResult::analyze(stmts);
let fn_returns_map: HashMap<Symbol, String> = stmts.iter().filter_map(|s| {
if let Stmt::FunctionDef { name, return_type: Some(rt), .. } = s {
Some((*name, codegen_type_expr(rt, interner)))
} else {
None
}
}).collect();
for stmt in stmts {
if let Stmt::FunctionDef { name, params, generics, body, return_type, is_native, native_path, is_exported, export_target, opt_flags } = stmt {
if mutual_tce_members.contains(name) {
if !mutual_tce_emitted.contains(name) {
if let Some((a, b)) = mutual_tce_pairs.iter().find(|(a, b)| *a == *name || *b == *name) {
output.push_str(&codegen_mutual_tce_pair(*a, *b, stmts, interner, &lww_fields, &mv_fields, &async_functions, &boxed_fields, registry, type_env));
mutual_tce_emitted.insert(*a);
mutual_tce_emitted.insert(*b);
}
}
} else {
output.push_str(&codegen_function_def(*name, generics, params, body, return_type.as_ref().copied(), *is_native, *native_path, *is_exported, *export_target, interner, &lww_fields, &mv_fields, &async_functions, &boxed_fields, registry, &pure_functions, type_env, &borrow_params_map, &mut_borrow_params_map, &liveness, opt_flags, &fn_returns_map));
}
}
}
if c_exports_exist {
let ref_types = collect_c_export_reference_types(stmts, interner, registry);
for ref_ty in &ref_types {
output.push_str(&codegen_c_accessors(ref_ty, interner, registry));
}
}
let main_stmts: Vec<&Stmt> = stmts.iter()
.filter(|s| !matches!(s, Stmt::FunctionDef { .. }))
.collect();
let mut main_mutable_vars = HashSet::new();
for stmt in &main_stmts {
collect_mutable_vars_stmt(stmt, &mut main_mutable_vars);
}
let single_char_vars = collect_single_char_text_vars(stmts, interner);
if requires_async(stmts) {
writeln!(output, "#[tokio::main]").unwrap();
writeln!(output, "async fn main() {{").unwrap();
} else {
writeln!(output, "fn main() {{").unwrap();
writeln!(output, " std::thread::Builder::new()").unwrap();
writeln!(output, " .stack_size(67_108_864)").unwrap();
writeln!(output, " .spawn(_logos_main)").unwrap();
writeln!(output, " .unwrap().join().unwrap();").unwrap();
writeln!(output, "}}").unwrap();
writeln!(output, "fn _logos_main() {{").unwrap();
}
if requires_vfs(stmts) {
writeln!(output, " let vfs: std::sync::Arc<dyn logicaffeine_system::fs::Vfs + Send + Sync> = std::sync::Arc::from(logicaffeine_system::fs::get_platform_vfs());").unwrap();
}
let mut main_ctx = RefinementContext::from_type_env(type_env);
let main_escaping = collect_escaping_collection_vars(stmts, interner);
main_ctx.set_escaping_vars(main_escaping);
for sym in &single_char_vars {
main_ctx.register_variable_type(*sym, "__single_char_u8".to_string());
}
for (fn_sym, indices) in &borrow_params_map {
let indices_str = indices.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(",");
main_ctx.register_variable_type(*fn_sym, format!("fn_borrow:{}", indices_str));
}
for (fn_sym, indices) in &mut_borrow_params_map {
let indices_str = indices.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(",");
main_ctx.register_variable_type(*fn_sym, format!("fn_mut_borrow:{}", indices_str));
}
for (fn_sym, rt_str) in &fn_returns_map {
main_ctx.register_fn_return(*fn_sym, rt_str.clone());
}
let mut main_synced_vars = HashSet::new(); let main_var_caps = analyze_variable_capabilities(stmts, interner);
{
let stmt_refs: Vec<&Stmt> = stmts.iter().collect();
let mut i = 0;
while i < stmt_refs.len() {
if matches!(stmt_refs[i], Stmt::FunctionDef { .. }) {
i += 1;
continue;
}
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry, type_env) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, i, interner, 1, &mut main_ctx) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_bare_slice_push_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_with_capacity_pattern(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry, type_env) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_merge_capacity_pattern(&stmt_refs, i, interner, 1, &mut main_ctx) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_string_with_capacity_pattern(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry, type_env) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_buffer_reuse_while(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry, type_env) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry, type_env) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&stmt_refs, i, interner, 1, &mut main_ctx) {
output.push_str(&code);
i += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
output.push_str(&code);
i += 1 + skip;
continue;
}
output.push_str(&codegen_stmt(stmt_refs[i], interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry, type_env));
i += 1;
}
}
writeln!(output, "}}").unwrap();
output
}
fn codegen_function_def(
name: Symbol,
generics: &[Symbol],
params: &[(Symbol, &TypeExpr)],
body: &[Stmt],
return_type: Option<&TypeExpr>,
is_native: bool,
native_path: Option<Symbol>,
is_exported: bool,
export_target: Option<Symbol>,
interner: &Interner,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>, async_functions: &HashSet<Symbol>, boxed_fields: &HashSet<(String, String, String)>, registry: &TypeRegistry, pure_functions: &HashSet<Symbol>,
type_env: &crate::analysis::types::TypeEnv,
borrow_params_map: &HashMap<Symbol, HashSet<usize>>,
mut_borrow_params_map: &HashMap<Symbol, HashSet<usize>>,
liveness: &LivenessResult,
opt_flags: &HashSet<OptFlag>,
fn_returns_map: &HashMap<Symbol, String>,
) -> String {
let mut output = String::new();
let names = RustNames::new(interner);
let raw_name = names.raw(name);
let func_name = names.ident(name);
let export_target_lower = export_target.map(|s| interner.resolve(s).to_lowercase());
let pipe_sender_params = collect_pipe_sender_params(body);
let is_c_export_early = is_exported && matches!(export_target_lower.as_deref(), None | Some("c"));
let no_tco = opt_flags.contains(&OptFlag::NoTCO) || opt_flags.contains(&OptFlag::NoOptimize);
let is_tce = !is_native && !is_c_export_early && !no_tco && is_tail_recursive(name, body);
let param_syms: Vec<Symbol> = params.iter().map(|(s, _)| *s).collect();
let acc_info = if !is_tce && !is_native && !is_c_export_early {
detect_accumulator_pattern(name, body)
} else {
None
};
let is_acc = acc_info.is_some();
let closed_form_info = if !is_tce && !is_acc && !is_native && !is_c_export_early {
detect_double_recursion_closed_form(name, params, body, interner)
} else {
None
};
let is_closed_form = closed_form_info.is_some();
let no_memo = opt_flags.contains(&OptFlag::NoMemo) || opt_flags.contains(&OptFlag::NoOptimize);
let is_memo = !is_tce && !is_acc && !is_closed_form && !is_native && !is_c_export_early && !no_memo
&& should_memoize(name, body, params, return_type, pure_functions.contains(&name), interner);
let needs_mut_params = is_tce || is_acc;
let no_peephole = opt_flags.contains(&OptFlag::NoPeephole) || opt_flags.contains(&OptFlag::NoOptimize);
let borrow_indices = borrow_params_map.get(&name).cloned().unwrap_or_default();
let mut_borrow_indices = mut_borrow_params_map.get(&name).cloned().unwrap_or_default();
let func_mutable_vars = collect_mutable_vars(body);
let params_str: Vec<String> = params.iter().enumerate()
.map(|(i, (param_name, param_type))| {
let pname = names.ident(*param_name);
let ty = codegen_type_expr(param_type, interner);
if pipe_sender_params.contains(param_name) {
format!("{}: tokio::sync::mpsc::Sender<{}>", pname, ty)
} else if borrow_indices.contains(&i) {
let slice_ty = vec_to_slice_type(&ty);
format!("{}: {}", pname, slice_ty)
} else if mut_borrow_indices.contains(&i) {
let slice_ty = vec_to_mut_slice_type(&ty);
format!("{}: {}", pname, slice_ty)
} else if needs_mut_params || func_mutable_vars.contains(param_name) {
format!("mut {}: {}", pname, ty)
} else {
format!("{}: {}", pname, ty)
}
})
.collect();
let has_mut_borrow = !mut_borrow_indices.is_empty();
let return_type_str = if has_mut_borrow {
None } else {
return_type
.map(|t| codegen_type_expr(t, interner))
.or_else(|| infer_return_type_from_body(body, interner))
};
let is_async = async_functions.contains(&name);
let fn_keyword = if is_async { "async fn" } else { "fn" };
let is_c_export = is_c_export_early;
let needs_c_marshaling = is_c_export && {
let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
let has_ref_param = params.iter().any(|(_, ty)| {
classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
});
let has_ref_return = return_type.map_or(false, |ty| {
classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
});
let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
let has_refinement_param = params.iter().any(|(_, ty)| {
matches!(ty, TypeExpr::Refinement { .. })
});
has_text_param || has_text_return || has_ref_param || has_ref_return
|| has_result_return || has_refinement_param
};
if needs_c_marshaling {
return codegen_c_export_with_marshaling(
name, params, body, return_type, interner,
lww_fields, mv_fields, async_functions, boxed_fields, registry, type_env,
);
}
let (vis_prefix, abi_prefix) = if is_exported {
match export_target_lower.as_deref() {
None | Some("c") => ("pub ", "extern \"C\" "),
Some("wasm") => ("pub ", ""),
_ => ("pub ", ""),
}
} else {
("", "")
};
let generics_str = if generics.is_empty() {
String::new()
} else {
let params_list: Vec<&str> = generics.iter()
.map(|sym| interner.resolve(*sym))
.collect();
format!("<{}>", params_list.join(", "))
};
let signature = if let Some(ref ret_ty) = return_type_str {
if ret_ty != "()" {
format!("{}{}{} {}{}({}) -> {}", vis_prefix, abi_prefix, fn_keyword, func_name, generics_str, params_str.join(", "), ret_ty)
} else {
format!("{}{}{} {}{}({})", vis_prefix, abi_prefix, fn_keyword, func_name, generics_str, params_str.join(", "))
}
} else {
format!("{}{}{} {}{}({})", vis_prefix, abi_prefix, fn_keyword, func_name, generics_str, params_str.join(", "))
};
if is_closed_form || (!is_tce && !is_acc && should_inline(name, body, is_native, is_exported, is_async)) {
writeln!(output, "#[inline]").unwrap();
}
if is_exported {
match export_target_lower.as_deref() {
None | Some("c") => {
writeln!(output, "#[export_name = \"logos_{}\"]", raw_name).unwrap();
}
Some("wasm") => {
writeln!(output, "#[wasm_bindgen]").unwrap();
}
_ => {}
}
}
if is_native {
let arg_names: Vec<&str> = params.iter()
.map(|(n, _)| interner.resolve(*n))
.collect();
if let Some(path_sym) = native_path {
let path = interner.resolve(path_sym);
let is_valid_path = !path.is_empty() && path.split("::").all(|seg| {
!seg.is_empty() && seg.chars().all(|c| c.is_alphanumeric() || c == '_')
});
if is_valid_path {
writeln!(output, "{} {{", signature).unwrap();
writeln!(output, " {}({})", path, arg_names.join(", ")).unwrap();
writeln!(output, "}}\n").unwrap();
} else {
writeln!(output, "{} {{", signature).unwrap();
writeln!(output, " compile_error!(\"Invalid native function path: '{}'. Path must be a valid Rust path like \\\"crate::module::function\\\".\")", path).unwrap();
writeln!(output, "}}\n").unwrap();
}
} else {
if let Some((module, core_fn)) = map_native_function(raw_name) {
writeln!(output, "{} {{", signature).unwrap();
writeln!(output, " logicaffeine_system::{}::{}({})", module, core_fn, arg_names.join(", ")).unwrap();
writeln!(output, "}}\n").unwrap();
} else {
writeln!(output, "{} {{", signature).unwrap();
writeln!(output, " compile_error!(\"Unknown system native function: '{}'. Use `is native \\\"crate::path\\\"` syntax for user-defined native functions.\")", raw_name).unwrap();
writeln!(output, "}}\n").unwrap();
}
}
} else {
writeln!(output, "{} {{", signature).unwrap();
let wrap_catch_unwind = is_c_export;
if wrap_catch_unwind {
writeln!(output, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
}
let mut func_ctx = RefinementContext::new();
let func_escaping = collect_escaping_collection_vars(body, interner);
func_ctx.set_escaping_vars(func_escaping);
let func_single_char_vars = collect_single_char_text_vars(body, interner);
for sym in &func_single_char_vars {
func_ctx.register_variable_type(*sym, "__single_char_u8".to_string());
}
let mut func_synced_vars = HashSet::new(); let func_var_caps = analyze_variable_capabilities(body, interner);
for (i, (param_name, param_type)) in params.iter().enumerate() {
let type_name = codegen_type_expr(param_type, interner);
if borrow_indices.contains(&i) {
func_ctx.register_variable_type(*param_name, vec_to_slice_type(&type_name));
} else if mut_borrow_indices.contains(&i) {
func_ctx.register_variable_type(*param_name, vec_to_mut_slice_type(&type_name));
} else {
func_ctx.register_variable_type(*param_name, type_name);
}
}
for (fn_sym, indices) in borrow_params_map {
let indices_str = indices.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(",");
func_ctx.register_variable_type(*fn_sym, format!("fn_borrow:{}", indices_str));
}
for (fn_sym, indices) in mut_borrow_params_map {
let indices_str = indices.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(",");
func_ctx.register_variable_type(*fn_sym, format!("fn_mut_borrow:{}", indices_str));
}
for (fn_sym, rt_str) in fn_returns_map {
func_ctx.register_fn_return(*fn_sym, rt_str.clone());
}
let func_pipe_vars = HashSet::new();
if is_tce {
writeln!(output, " loop {{").unwrap();
let stmt_refs: Vec<&Stmt> = body.iter().collect();
let mut si = 0;
while si < stmt_refs.len() {
if !no_peephole {
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_bare_slice_push_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_with_capacity_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_merge_capacity_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_string_with_capacity_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_buffer_reuse_while(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
}
output.push_str(&codegen_stmt_tce(stmt_refs[si], name, ¶m_syms, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env));
si += 1;
}
writeln!(output, " }}").unwrap();
} else if let Some(ref acc) = acc_info {
writeln!(output, " let mut __acc: i64 = {};", acc.identity).unwrap();
writeln!(output, " loop {{").unwrap();
let stmt_refs: Vec<&Stmt> = body.iter().collect();
let mut si = 0;
while si < stmt_refs.len() {
if !no_peephole {
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_bare_slice_push_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_with_capacity_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_merge_capacity_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_string_with_capacity_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_buffer_reuse_while(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
}
output.push_str(&codegen_stmt_acc(stmt_refs[si], name, ¶m_syms, acc, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env));
si += 1;
}
writeln!(output, " }}").unwrap();
} else if let Some(ref cf) = closed_form_info {
let param_name = names.ident(params[0].0);
let base_plus_k = cf.base + cf.k;
writeln!(output, " if {} == 0 {{ return {}; }}", param_name, cf.base).unwrap();
if cf.k == 0 {
writeln!(output, " ({}i64 << {})", base_plus_k, param_name).unwrap();
} else {
writeln!(output, " ({}i64 << {}) - {}", base_plus_k, param_name, cf.k).unwrap();
}
} else if is_memo {
let ret_ty = return_type_str.as_deref().unwrap_or("i64");
let memo_name = format!("__MEMO_{}", func_name.to_uppercase());
let (key_type, key_expr, copy_method) = if params.len() == 1 {
let ty = codegen_type_expr(params[0].1, interner);
let pname = interner.resolve(params[0].0).to_string();
let copy = if is_copy_type_expr(params[0].1, interner) { "copied" } else { "cloned" };
(ty, pname, copy)
} else {
let types: Vec<String> = params.iter().map(|(_, t)| codegen_type_expr(t, interner)).collect();
let names: Vec<String> = params.iter().map(|(n, _)| interner.resolve(*n).to_string()).collect();
let copy = if params.iter().all(|(_, t)| is_copy_type_expr(t, interner)) { "copied" } else { "cloned" };
(format!("({})", types.join(", ")), format!("({})", names.join(", ")), copy)
};
writeln!(output, " use std::cell::RefCell;").unwrap();
writeln!(output, " thread_local! {{").unwrap();
writeln!(output, " static {}: RefCell<FxHashMap<{}, {}>> = RefCell::new(FxHashMap::default());", memo_name, key_type, ret_ty).unwrap();
writeln!(output, " }}").unwrap();
writeln!(output, " if let Some(__v) = {}.with(|c| c.borrow().get(&{}).{}()) {{", memo_name, key_expr, copy_method).unwrap();
writeln!(output, " return __v;").unwrap();
writeln!(output, " }}").unwrap();
writeln!(output, " let __memo_result = (|| -> {} {{", ret_ty).unwrap();
let stmt_refs: Vec<&Stmt> = body.iter().collect();
let mut si = 0;
while si < stmt_refs.len() {
if !no_peephole {
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_bare_slice_push_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_with_capacity_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_merge_capacity_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_string_with_capacity_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_buffer_reuse_while(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&stmt_refs, si, interner, 2, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
}
output.push_str(&codegen_stmt(stmt_refs[si], interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env));
si += 1;
}
writeln!(output, " }})();").unwrap();
writeln!(output, " {}.with(|c| c.borrow_mut().insert({}, __memo_result));", memo_name, key_expr).unwrap();
writeln!(output, " __memo_result").unwrap();
} else {
let stmt_refs: Vec<&Stmt> = body.iter().collect();
let mut si = 0;
while si < stmt_refs.len() {
if !no_peephole {
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_bare_slice_push_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_with_capacity_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_merge_capacity_pattern(&stmt_refs, si, interner, 1, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_string_with_capacity_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_buffer_reuse_while(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&stmt_refs, si, interner, 1, &mut func_ctx) {
output.push_str(&code);
si += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
output.push_str(&code);
si += 1 + skip;
continue;
}
}
func_ctx.set_live_vars_after(liveness.live_after(name, si).clone());
output.push_str(&codegen_stmt(stmt_refs[si], interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env));
si += 1;
}
}
if wrap_catch_unwind {
writeln!(output, " }})) {{").unwrap();
writeln!(output, " Ok(__v) => __v,").unwrap();
writeln!(output, " Err(__panic) => {{").unwrap();
writeln!(output, " let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
writeln!(output, " logos_set_last_error(__msg);").unwrap();
if let Some(ref ret_str) = return_type_str {
if ret_str != "()" {
writeln!(output, " Default::default()").unwrap();
}
}
writeln!(output, " }}").unwrap();
writeln!(output, " }}").unwrap();
}
writeln!(output, "}}\n").unwrap();
}
output
}
fn map_native_function(name: &str) -> Option<(&'static str, &'static str)> {
match name {
"read" => Some(("file", "read")),
"write" => Some(("file", "write")),
"now" => Some(("time", "now")),
"sleep" => Some(("time", "sleep")),
"randomInt" => Some(("random", "randomInt")),
"randomFloat" => Some(("random", "randomFloat")),
"get" => Some(("env", "get")),
"args" => Some(("env", "args")),
"parseInt" => Some(("text", "parseInt")),
"parseFloat" => Some(("text", "parseFloat")),
"chr" => Some(("text", "chr")),
"format" => Some(("fmt", "format")),
_ => None,
}
}