1use crate::ast::{FnDef, Module, TopLevel, TypeDef, TypeVariant};
2
3#[derive(Debug, Clone)]
5pub struct ModuleTypeDef {
6 pub bare_name: String,
7 pub kind: ModuleTypeKind,
8}
9
10#[derive(Debug, Clone)]
11pub enum ModuleTypeKind {
12 Record { field_names: Vec<String> },
13 Sum { variant_names: Vec<String> },
14}
15
16pub fn collect_module_types(items: &[TopLevel]) -> Vec<ModuleTypeDef> {
19 items
20 .iter()
21 .filter_map(|item| {
22 let TopLevel::TypeDef(td) = item else {
23 return None;
24 };
25 Some(match td {
26 TypeDef::Product { name, fields, .. } => ModuleTypeDef {
27 bare_name: name.clone(),
28 kind: ModuleTypeKind::Record {
29 field_names: fields.iter().map(|(n, _)| n.clone()).collect(),
30 },
31 },
32 TypeDef::Sum { name, variants, .. } => ModuleTypeDef {
33 bare_name: name.clone(),
34 kind: ModuleTypeKind::Sum {
35 variant_names: variants.iter().map(|v| v.name.clone()).collect(),
36 },
37 },
38 })
39 })
40 .collect()
41}
42
43pub fn is_exposed(name: &str, exposes: Option<&[String]>) -> bool {
47 match exposes {
48 Some(list) => list.iter().any(|e| e == name),
49 None => !name.starts_with('_'),
50 }
51}
52
53pub struct ExportedTypeDef<'a> {
59 pub def: &'a TypeDef,
60 pub is_opaque: bool,
61}
62
63pub struct ModuleExports<'a> {
65 pub functions: Vec<&'a FnDef>,
66 pub types: Vec<ExportedTypeDef<'a>>,
67}
68
69pub fn module_decl(items: &[TopLevel]) -> Option<&Module> {
71 items.iter().find_map(|i| {
72 if let TopLevel::Module(m) = i {
73 Some(m)
74 } else {
75 None
76 }
77 })
78}
79
80pub fn collect_module_exports<'a>(items: &'a [TopLevel]) -> ModuleExports<'a> {
83 let module = module_decl(items);
84
85 let exposes: Option<&[String]> = module.and_then(|m| {
86 if m.exposes.is_empty() {
87 None
88 } else {
89 Some(m.exposes.as_slice())
90 }
91 });
92
93 let opaque_names: Vec<&str> = module
94 .map(|m| m.exposes_opaque.iter().map(|s| s.as_str()).collect())
95 .unwrap_or_default();
96
97 let functions = items
98 .iter()
99 .filter_map(|item| {
100 let TopLevel::FnDef(fd) = item else {
101 return None;
102 };
103 if is_exposed(&fd.name, exposes) {
104 Some(fd)
105 } else {
106 None
107 }
108 })
109 .collect();
110
111 let types = items
112 .iter()
113 .filter_map(|item| {
114 let TopLevel::TypeDef(td) = item else {
115 return None;
116 };
117 let name = match td {
118 TypeDef::Sum { name, .. } | TypeDef::Product { name, .. } => name.as_str(),
119 };
120 let is_opaque = opaque_names.contains(&name);
121 if is_exposed(name, exposes) || is_opaque {
122 Some(ExportedTypeDef { def: td, is_opaque })
123 } else {
124 None
125 }
126 })
127 .collect();
128
129 ModuleExports { functions, types }
130}
131
132pub fn collect_all_module_symbols<'a>(items: &'a [TopLevel]) -> ModuleExports<'a> {
135 let functions = items
136 .iter()
137 .filter_map(|item| {
138 if let TopLevel::FnDef(fd) = item {
139 Some(fd)
140 } else {
141 None
142 }
143 })
144 .collect();
145
146 let module = module_decl(items);
147 let opaque_names: Vec<&str> = module
148 .map(|m| m.exposes_opaque.iter().map(|s| s.as_str()).collect())
149 .unwrap_or_default();
150
151 let types = items
152 .iter()
153 .filter_map(|item| {
154 let TopLevel::TypeDef(td) = item else {
155 return None;
156 };
157 let name = match td {
158 TypeDef::Sum { name, .. } | TypeDef::Product { name, .. } => name.as_str(),
159 };
160 Some(ExportedTypeDef {
161 def: td,
162 is_opaque: opaque_names.contains(&name),
163 })
164 })
165 .collect();
166
167 ModuleExports { functions, types }
168}
169
170pub fn qualified_name(module: &str, name: &str) -> String {
176 format!("{}.{}", module, name)
177}
178
179pub fn member_key(type_name: &str, member: &str) -> String {
181 format!("{}.{}", type_name, member)
182}
183
184pub fn qualified_member_key(module: &str, type_name: &str, member: &str) -> String {
186 format!("{}.{}.{}", module, type_name, member)
187}
188
189#[derive(Debug, Clone)]
195pub struct SymbolEntry {
196 pub id: u32,
197 pub canonical_name: String,
198 pub alias: Option<String>,
199 pub module: String,
200 pub kind: SymbolKind,
201}
202
203#[derive(Debug, Clone)]
204pub enum SymbolKind {
205 Function {
206 name: String,
207 params: Vec<(String, String)>,
208 return_type: String,
209 effects: Vec<String>,
210 },
211 OpaqueType {
212 name: String,
213 },
214 SumType {
215 name: String,
216 variants: Vec<String>,
217 },
218 ProductType {
224 name: String,
225 fields: Vec<String>,
226 },
227 Constructor {
228 type_name: String,
229 variant_name: String,
230 field_types: Vec<String>,
231 },
232 RecordField {
233 type_name: String,
234 field_name: String,
235 field_type: String,
236 },
237}
238
239#[derive(Debug, Clone, Default)]
241pub struct SymbolRegistry {
242 pub entries: Vec<SymbolEntry>,
243}
244
245impl SymbolRegistry {
246 pub fn from_modules(modules: &[(String, Vec<TopLevel>)]) -> Self {
248 let mut entries = Vec::new();
249 for (module_name, items) in modules {
250 let exports = collect_module_exports(items);
251 Self::collect_from_exports(module_name, &exports, &mut entries);
252 }
253 SymbolRegistry { entries }
254 }
255
256 pub fn from_modules_all(modules: &[(String, Vec<TopLevel>)]) -> Self {
259 let mut entries = Vec::new();
260 for (module_name, items) in modules {
261 let all = collect_all_module_symbols(items);
262 Self::collect_from_exports(module_name, &all, &mut entries);
263 }
264 SymbolRegistry { entries }
265 }
266
267 fn collect_from_exports(
268 module_name: &str,
269 exports: &ModuleExports<'_>,
270 entries: &mut Vec<SymbolEntry>,
271 ) {
272 for fd in &exports.functions {
273 let id = entries.len() as u32;
274 entries.push(SymbolEntry {
275 id,
276 canonical_name: qualified_name(module_name, &fd.name),
277 alias: None,
278 module: module_name.to_string(),
279 kind: SymbolKind::Function {
280 name: fd.name.clone(),
281 params: fd.params.clone(),
282 return_type: fd.return_type.clone(),
283 effects: fd.effects.iter().map(|e| e.node.clone()).collect(),
284 },
285 });
286 }
287
288 for et in &exports.types {
289 match et.def {
290 TypeDef::Sum {
291 name: type_name,
292 variants,
293 ..
294 } => {
295 if et.is_opaque {
296 let id = entries.len() as u32;
297 entries.push(SymbolEntry {
298 id,
299 canonical_name: qualified_name(module_name, type_name),
300 alias: Some(type_name.clone()),
301 module: module_name.to_string(),
302 kind: SymbolKind::OpaqueType {
303 name: type_name.clone(),
304 },
305 });
306 } else {
307 let id = entries.len() as u32;
308 entries.push(SymbolEntry {
309 id,
310 canonical_name: qualified_name(module_name, type_name),
311 alias: Some(type_name.clone()),
312 module: module_name.to_string(),
313 kind: SymbolKind::SumType {
314 name: type_name.clone(),
315 variants: variants.iter().map(|v| v.name.clone()).collect(),
316 },
317 });
318 Self::collect_variant_entries(
319 module_name,
320 type_name.as_str(),
321 variants,
322 entries,
323 );
324 }
325 }
326 TypeDef::Product {
327 name: type_name,
328 fields,
329 ..
330 } => {
331 if et.is_opaque {
332 let id = entries.len() as u32;
333 entries.push(SymbolEntry {
334 id,
335 canonical_name: qualified_name(module_name, type_name),
336 alias: Some(type_name.clone()),
337 module: module_name.to_string(),
338 kind: SymbolKind::OpaqueType {
339 name: type_name.clone(),
340 },
341 });
342 } else {
343 let id = entries.len() as u32;
349 entries.push(SymbolEntry {
350 id,
351 canonical_name: qualified_name(module_name, type_name),
352 alias: Some(type_name.clone()),
353 module: module_name.to_string(),
354 kind: SymbolKind::ProductType {
355 name: type_name.clone(),
356 fields: fields.iter().map(|(n, _)| n.clone()).collect(),
357 },
358 });
359 for (field_name, ty_str) in fields {
360 let id = entries.len() as u32;
361 let canonical =
362 qualified_member_key(module_name, type_name, field_name);
363 let alias = member_key(type_name, field_name);
364 entries.push(SymbolEntry {
365 id,
366 canonical_name: canonical,
367 alias: Some(alias),
368 module: module_name.to_string(),
369 kind: SymbolKind::RecordField {
370 type_name: type_name.clone(),
371 field_name: field_name.clone(),
372 field_type: ty_str.clone(),
373 },
374 });
375 }
376 }
377 }
378 }
379 }
380 }
381
382 fn collect_variant_entries(
383 module_name: &str,
384 type_name: &str,
385 variants: &[TypeVariant],
386 entries: &mut Vec<SymbolEntry>,
387 ) {
388 for variant in variants {
389 let id = entries.len() as u32;
390 let canonical = qualified_member_key(module_name, type_name, &variant.name);
391 let alias = member_key(type_name, &variant.name);
392 entries.push(SymbolEntry {
393 id,
394 canonical_name: canonical,
395 alias: Some(alias),
396 module: module_name.to_string(),
397 kind: SymbolKind::Constructor {
398 type_name: type_name.to_string(),
399 variant_name: variant.name.clone(),
400 field_types: variant.fields.clone(),
401 },
402 });
403 }
404 }
405}