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