1mod builtins;
5mod expr;
6mod liveness;
7mod pattern;
8mod project;
9mod runtime;
10mod syntax;
11mod toplevel;
12mod types;
13
14use std::collections::HashSet;
15
16use crate::ast::TopLevel;
17use crate::codegen::{CodegenContext, ProjectOutput};
18use crate::types::Type;
19
20pub fn transpile(ctx: &CodegenContext) -> ProjectOutput {
22 let mut sections = Vec::new();
23
24 sections.push("#![allow(unused_variables, unused_mut, dead_code, unused_imports, unused_parens, non_snake_case, non_camel_case_types, unreachable_patterns)]".to_string());
26 sections.push("use std::collections::HashMap;".to_string());
27 sections.push(String::new());
28
29 sections.push(runtime::generate_runtime());
31 sections.push(String::new());
32
33 let used_services = detect_used_services(ctx);
35 let needs_http_types = needs_named_type(ctx, "Header")
36 || needs_named_type(ctx, "HttpResponse")
37 || needs_named_type(ctx, "HttpRequest");
38 let needs_tcp_types = needs_named_type(ctx, "Tcp.Connection");
39
40 let has_tcp_runtime = used_services.contains("Tcp");
42 let has_http_runtime = used_services.contains("Http");
43 let has_http_server_runtime = used_services.contains("HttpServer");
44
45 let has_tcp_types = has_tcp_runtime || needs_tcp_types;
46 let has_http_types = has_http_runtime || has_http_server_runtime || needs_http_types;
47 let has_http_server_types = has_http_server_runtime || needs_named_type(ctx, "HttpRequest");
48
49 if has_tcp_types {
50 sections.push(runtime::generate_tcp_types());
51 sections.push(String::new());
52 }
53
54 if has_http_types {
55 sections.push(runtime::generate_http_types());
56 sections.push(String::new());
57 }
58
59 if has_http_server_types {
60 sections.push(runtime::generate_http_server_types());
61 sections.push(String::new());
62 }
63
64 if has_tcp_runtime {
66 sections.push(runtime::generate_tcp_runtime());
67 sections.push(String::new());
68 }
69
70 if has_http_runtime {
71 sections.push(runtime::generate_http_runtime());
72 sections.push(String::new());
73 }
74
75 if has_http_server_runtime {
76 sections.push(runtime::generate_http_server_runtime());
77 sections.push(String::new());
78 }
79
80 for module in &ctx.modules {
82 for td in &module.type_defs {
83 sections.push(toplevel::emit_type_def(td));
84 sections.push(String::new());
85 }
86 }
87
88 for module in &ctx.modules {
90 for fd in &module.fn_defs {
91 let is_memo = ctx.memo_fns.contains(&fd.name);
92 sections.push(toplevel::emit_fn_def(fd, is_memo, ctx));
93 sections.push(String::new());
94 }
95 }
96
97 for td in &ctx.type_defs {
99 sections.push(toplevel::emit_type_def(td));
100 sections.push(String::new());
101 }
102
103 for fd in &ctx.fn_defs {
105 if fd.name == "main" {
106 continue;
107 }
108 let is_memo = ctx.memo_fns.contains(&fd.name);
109 sections.push(toplevel::emit_fn_def(fd, is_memo, ctx));
110 sections.push(String::new());
111 }
112
113 let main_fn = ctx.fn_defs.iter().find(|fd| fd.name == "main");
115 let top_level_stmts: Vec<_> = ctx
116 .items
117 .iter()
118 .filter_map(|item| {
119 if let TopLevel::Stmt(stmt) = item {
120 Some(stmt)
121 } else {
122 None
123 }
124 })
125 .collect();
126
127 sections.push(toplevel::emit_main(main_fn, &top_level_stmts, ctx));
128 sections.push(String::new());
129
130 let verify_blocks: Vec<_> = ctx
132 .items
133 .iter()
134 .filter_map(|item| {
135 if let TopLevel::Verify(vb) = item {
136 Some(vb)
137 } else {
138 None
139 }
140 })
141 .collect();
142
143 if !verify_blocks.is_empty() {
144 sections.push(toplevel::emit_verify_blocks(&verify_blocks, ctx));
145 }
146
147 let main_rs = sections.join("\n");
148 let cargo_toml = project::generate_cargo_toml(&ctx.project_name, &used_services);
149
150 ProjectOutput {
151 files: vec![
152 ("Cargo.toml".to_string(), cargo_toml),
153 ("src/main.rs".to_string(), main_rs),
154 ],
155 }
156}
157
158fn detect_used_services(ctx: &CodegenContext) -> HashSet<String> {
160 let mut services = HashSet::new();
161 for item in &ctx.items {
162 if let TopLevel::FnDef(fd) = item {
163 for eff in &fd.effects {
164 services.insert(eff.clone());
165 }
166 }
167 }
168 for module in &ctx.modules {
169 for fd in &module.fn_defs {
170 for eff in &fd.effects {
171 services.insert(eff.clone());
172 }
173 }
174 }
175 services
176}
177
178fn needs_named_type(ctx: &CodegenContext, wanted: &str) -> bool {
179 ctx.fn_sigs.values().any(|(params, ret, _effects)| {
180 params.iter().any(|p| type_contains_named(p, wanted)) || type_contains_named(ret, wanted)
181 })
182}
183
184fn type_contains_named(ty: &Type, wanted: &str) -> bool {
185 match ty {
186 Type::Named(name) => name == wanted,
187 Type::Result(ok, err) => {
188 type_contains_named(ok, wanted) || type_contains_named(err, wanted)
189 }
190 Type::Option(inner) | Type::List(inner) => type_contains_named(inner, wanted),
191 Type::Tuple(items) => items.iter().any(|t| type_contains_named(t, wanted)),
192 Type::Map(k, v) => type_contains_named(k, wanted) || type_contains_named(v, wanted),
193 Type::Fn(params, ret, _effects) => {
194 params.iter().any(|t| type_contains_named(t, wanted))
195 || type_contains_named(ret, wanted)
196 }
197 Type::Int | Type::Float | Type::Str | Type::Bool | Type::Unit | Type::Unknown => false,
198 }
199}