1use std::collections::{BTreeMap, BTreeSet};
17
18use lex_vcs::{default_import_alias, OpLog, OperationKind};
19
20use crate::store::{Store, StoreError};
21
22#[derive(Debug, Default, Clone)]
25pub struct PackageHead {
26 pub map: BTreeMap<String, String>,
28 pub sig_files: BTreeMap<String, String>,
30 pub flat_imports: BTreeMap<String, String>,
32 pub file_imports: BTreeMap<String, BTreeMap<String, String>>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38pub enum RenderedSource {
39 Single(String),
40 Multi(BTreeMap<String, String>),
41}
42
43pub fn package_head_at_op(store: &Store, head_op: &str) -> Result<PackageHead, StoreError> {
47 let log = OpLog::open(store.root())?;
48 let mut head = PackageHead::default();
49 for rec in log.walk_forward(&head_op.to_string(), None)? {
50 crate::branches::apply_transition(&mut head.map, &rec.produces);
51 match &rec.op.kind {
52 OperationKind::AddFunction { sig_id, in_file: Some(f), .. }
53 | OperationKind::AddType { sig_id, in_file: Some(f), .. } => {
54 head.sig_files.insert(sig_id.clone(), f.clone());
55 }
56 OperationKind::AddImport { in_file, module, alias } => {
57 let alias = alias.clone().unwrap_or_else(|| default_import_alias(module));
58 head.flat_imports.insert(module.clone(), alias.clone());
59 head.file_imports.entry(in_file.clone()).or_default().insert(module.clone(), alias);
60 }
61 OperationKind::RemoveImport { in_file, module } => {
62 head.flat_imports.remove(module);
63 if let Some(m) = head.file_imports.get_mut(in_file) {
64 m.remove(module);
65 }
66 }
67 OperationKind::RenameSymbol { from, to, .. } => {
68 if let Some(f) = head.sig_files.remove(from) {
69 head.sig_files.insert(to.clone(), f);
70 }
71 }
72 _ => {}
73 }
74 }
75 Ok(head)
76}
77
78pub fn render_source(store: &Store, head: &PackageHead) -> Result<RenderedSource, StoreError> {
81 let multi = !head.map.is_empty() && head.map.keys().all(|s| head.sig_files.contains_key(s));
82 if multi {
83 Ok(RenderedSource::Multi(render_multifile(store, head)?))
84 } else {
85 Ok(RenderedSource::Single(render_singlefile(store, head)?))
86 }
87}
88
89fn render_singlefile(store: &Store, head: &PackageHead) -> Result<String, StoreError> {
93 let mut stages: Vec<lex_ast::Stage> = Vec::new();
94 for (reference, alias) in &head.flat_imports {
95 stages.push(lex_ast::Stage::Import(lex_ast::Import {
96 reference: reference.clone(),
97 alias: alias.clone(),
98 }));
99 }
100 let pairs: Vec<(String, String)> = head.map.iter().map(|(s, st)| (s.clone(), st.clone())).collect();
101 for ast in store.get_asts_for_sigs_bulk(&pairs) {
102 stages.push(ast?);
103 }
104 Ok(lex_ast::print_stages(&stages))
105}
106
107fn render_multifile(store: &Store, head: &PackageHead) -> Result<BTreeMap<String, String>, StoreError> {
109 let mut prefix_to_file: BTreeMap<String, String> = BTreeMap::new();
110 let mut by_file: BTreeMap<String, Vec<lex_ast::Stage>> = BTreeMap::new();
111 let pairs: Vec<(String, String)> = head.map.iter().map(|(s, st)| (s.clone(), st.clone())).collect();
115 let asts = store.get_asts_for_sigs_bulk(&pairs);
116 for ((sig, _), ast) in pairs.iter().zip(asts) {
117 let stage = ast?;
118 let file = head.sig_files.get(sig).cloned().unwrap_or_default();
119 if let Some(prefix) = stage_prefix(&stage) {
120 prefix_to_file.insert(prefix, file.clone());
121 }
122 by_file.entry(file).or_default().push(stage);
123 }
124
125 let mut out: BTreeMap<String, String> = BTreeMap::new();
126 for (file, stages) in &by_file {
127 let own_prefix = stages.iter().find_map(stage_prefix).unwrap_or_default();
128 let mut bound_locals = BTreeSet::new();
129 for s in stages {
130 collect_bound_locals(s, &mut bound_locals);
131 }
132 let mut rw = FileRewrite {
133 own_prefix: &own_prefix,
134 own_file: file,
135 prefix_to_file: &prefix_to_file,
136 bound_locals: &bound_locals,
137 local_imports: BTreeMap::new(),
138 };
139 let rewritten: Vec<lex_ast::Stage> = stages
140 .iter()
141 .cloned()
142 .map(|mut s| {
143 rw.rewrite_stage(&mut s);
144 s
145 })
146 .collect();
147
148 let mut imports: BTreeMap<String, String> = head.file_imports.get(file).cloned().unwrap_or_default();
149 imports.extend(rw.local_imports);
150
151 let mut out_stages: Vec<lex_ast::Stage> = Vec::new();
152 for (reference, alias) in &imports {
153 out_stages.push(lex_ast::Stage::Import(lex_ast::Import {
154 reference: reference.clone(),
155 alias: alias.clone(),
156 }));
157 }
158 out_stages.extend(rewritten);
159 out.insert(file.clone(), lex_ast::print_stages(&out_stages));
160 }
161 Ok(out)
162}
163
164fn stage_prefix(s: &lex_ast::Stage) -> Option<String> {
167 let name = match s {
168 lex_ast::Stage::FnDecl(fd) => &fd.name,
169 lex_ast::Stage::TypeDecl(td) => &td.name,
170 lex_ast::Stage::Import(_) => return None,
171 };
172 name.split_once('.').map(|(p, _)| p.to_string())
173}
174
175fn relative_import(from: &str, to: &str) -> (String, String) {
177 let from_dir: Vec<&str> = from
178 .rsplit_once('/')
179 .map(|(d, _)| d)
180 .unwrap_or("")
181 .split('/')
182 .filter(|s| !s.is_empty())
183 .collect();
184 let to_noext = to.strip_suffix(".lex").unwrap_or(to);
185 let to_parts: Vec<&str> = to_noext.split('/').filter(|s| !s.is_empty()).collect();
186 let alias = to_parts.last().copied().unwrap_or("mod").to_string();
187 let mut i = 0;
188 while i < from_dir.len() && i + 1 < to_parts.len() && from_dir[i] == to_parts[i] {
189 i += 1;
190 }
191 let ups = from_dir.len() - i;
192 let mut rel = String::new();
193 if ups == 0 {
194 rel.push_str("./");
195 } else {
196 for _ in 0..ups {
197 rel.push_str("../");
198 }
199 }
200 rel.push_str(&to_parts[i..].join("/"));
201 (rel, alias)
202}
203
204fn collect_bound_locals(s: &lex_ast::Stage, out: &mut BTreeSet<String>) {
205 if let lex_ast::Stage::FnDecl(fd) = s {
206 for p in &fd.params {
207 out.insert(p.name.clone());
208 }
209 collect_expr_locals(&fd.body, out);
210 for ex in &fd.examples {
211 for a in &ex.args {
212 collect_expr_locals(a, out);
213 }
214 collect_expr_locals(&ex.expected, out);
215 }
216 }
217}
218
219fn collect_expr_locals(e: &lex_ast::CExpr, out: &mut BTreeSet<String>) {
220 use lex_ast::CExpr::*;
221 match e {
222 Let { name, value, body, .. } => {
223 out.insert(name.clone());
224 collect_expr_locals(value, out);
225 collect_expr_locals(body, out);
226 }
227 Lambda { params, body, .. } => {
228 for p in params {
229 out.insert(p.name.clone());
230 }
231 collect_expr_locals(body, out);
232 }
233 Match { scrutinee, arms } => {
234 collect_expr_locals(scrutinee, out);
235 for arm in arms {
236 collect_pattern_locals(&arm.pattern, out);
237 collect_expr_locals(&arm.body, out);
238 }
239 }
240 Call { callee, args } => {
241 collect_expr_locals(callee, out);
242 for a in args {
243 collect_expr_locals(a, out);
244 }
245 }
246 Block { statements, result } => {
247 for s in statements {
248 collect_expr_locals(s, out);
249 }
250 collect_expr_locals(result, out);
251 }
252 Constructor { args, .. } => {
253 for a in args {
254 collect_expr_locals(a, out);
255 }
256 }
257 RecordLit { fields } => {
258 for f in fields {
259 collect_expr_locals(&f.value, out);
260 }
261 }
262 TupleLit { items } | ListLit { items } => {
263 for i in items {
264 collect_expr_locals(i, out);
265 }
266 }
267 FieldAccess { value, .. } => collect_expr_locals(value, out),
268 BinOp { lhs, rhs, .. } => {
269 collect_expr_locals(lhs, out);
270 collect_expr_locals(rhs, out);
271 }
272 UnaryOp { expr, .. } => collect_expr_locals(expr, out),
273 Return { value } => collect_expr_locals(value, out),
274 Var { .. } | Literal { .. } => {}
275 }
276}
277
278fn collect_pattern_locals(p: &lex_ast::Pattern, out: &mut BTreeSet<String>) {
279 use lex_ast::Pattern::*;
280 match p {
281 PVar { name } => {
282 out.insert(name.clone());
283 }
284 PConstructor { args, .. } => {
285 for a in args {
286 collect_pattern_locals(a, out);
287 }
288 }
289 PRecord { fields } => {
290 for f in fields {
291 collect_pattern_locals(&f.pattern, out);
292 }
293 }
294 PTuple { items } => {
295 for i in items {
296 collect_pattern_locals(i, out);
297 }
298 }
299 PLiteral { .. } | PWild => {}
300 }
301}
302
303struct FileRewrite<'a> {
304 own_prefix: &'a str,
305 own_file: &'a str,
306 prefix_to_file: &'a BTreeMap<String, String>,
307 bound_locals: &'a BTreeSet<String>,
308 local_imports: BTreeMap<String, String>,
309}
310
311impl FileRewrite<'_> {
312 fn rename(&mut self, name: &str) -> String {
316 if let Some(rest) = name.strip_prefix(&format!("{}.", self.own_prefix)) {
317 return rest.to_string();
318 }
319 if let Some((q, rest)) = name.split_once('.') {
320 if q != self.own_prefix {
321 if let Some(other_file) = self.prefix_to_file.get(q) {
322 let (import_ref, stem) = relative_import(self.own_file, other_file);
323 let alias = if self.bound_locals.contains(&stem) {
324 q.to_string()
325 } else {
326 stem
327 };
328 self.local_imports.insert(import_ref, alias.clone());
329 return format!("{alias}.{rest}");
330 }
331 }
332 }
333 name.to_string()
334 }
335
336 fn rewrite_stage(&mut self, s: &mut lex_ast::Stage) {
337 match s {
338 lex_ast::Stage::FnDecl(fd) => {
339 fd.name = self.rename(&fd.name);
340 for p in &mut fd.params {
341 self.rewrite_type(&mut p.ty);
342 }
343 self.rewrite_type(&mut fd.return_type);
344 self.rewrite_expr(&mut fd.body);
345 for ex in &mut fd.examples {
346 for a in &mut ex.args {
347 self.rewrite_expr(a);
348 }
349 self.rewrite_expr(&mut ex.expected);
350 }
351 }
352 lex_ast::Stage::TypeDecl(td) => {
353 td.name = self.rename(&td.name);
354 self.rewrite_type(&mut td.definition);
355 }
356 lex_ast::Stage::Import(_) => {}
357 }
358 }
359
360 fn rewrite_expr(&mut self, e: &mut lex_ast::CExpr) {
361 use lex_ast::CExpr::*;
362 match e {
363 Var { name } => *name = self.rename(name),
364 Literal { .. } => {}
365 Call { callee, args } => {
366 self.rewrite_expr(callee);
367 for a in args {
368 self.rewrite_expr(a);
369 }
370 }
371 Let { value, body, ty, .. } => {
372 if let Some(t) = ty {
373 self.rewrite_type(t);
374 }
375 self.rewrite_expr(value);
376 self.rewrite_expr(body);
377 }
378 Match { scrutinee, arms } => {
379 self.rewrite_expr(scrutinee);
380 for arm in arms {
381 self.rewrite_expr(&mut arm.body);
382 }
383 }
384 Block { statements, result } => {
385 for s in statements {
386 self.rewrite_expr(s);
387 }
388 self.rewrite_expr(result);
389 }
390 Constructor { args, .. } => {
391 for a in args {
392 self.rewrite_expr(a);
393 }
394 }
395 RecordLit { fields } => {
396 for f in fields {
397 self.rewrite_expr(&mut f.value);
398 }
399 }
400 TupleLit { items } | ListLit { items } => {
401 for i in items {
402 self.rewrite_expr(i);
403 }
404 }
405 FieldAccess { value, .. } => self.rewrite_expr(value),
406 Lambda { params, return_type, body, .. } => {
407 for p in params {
408 self.rewrite_type(&mut p.ty);
409 }
410 self.rewrite_type(return_type);
411 self.rewrite_expr(body);
412 }
413 BinOp { lhs, rhs, .. } => {
414 self.rewrite_expr(lhs);
415 self.rewrite_expr(rhs);
416 }
417 UnaryOp { expr, .. } => self.rewrite_expr(expr),
418 Return { value } => self.rewrite_expr(value),
419 }
420 }
421
422 fn rewrite_type(&mut self, t: &mut lex_ast::TypeExpr) {
423 use lex_ast::TypeExpr::*;
424 match t {
425 Named { name, args } => {
426 *name = self.rename(name);
427 for a in args {
428 self.rewrite_type(a);
429 }
430 }
431 Record { fields } => {
432 for f in fields {
433 self.rewrite_type(&mut f.ty);
434 }
435 }
436 Tuple { items } => {
437 for i in items {
438 self.rewrite_type(i);
439 }
440 }
441 Function { params, ret, .. } => {
442 for p in params {
443 self.rewrite_type(p);
444 }
445 self.rewrite_type(ret);
446 }
447 Union { variants } => {
448 for v in variants {
449 if let Some(pl) = &mut v.payload {
450 self.rewrite_type(pl);
451 }
452 }
453 }
454 RecordWithSpreads { spreads, fields } => {
455 for s in spreads {
456 *s = self.rename(s);
457 }
458 for f in fields {
459 self.rewrite_type(&mut f.ty);
460 }
461 }
462 Refined { base, predicate, .. } => {
463 self.rewrite_type(base);
464 self.rewrite_expr(predicate);
465 }
466 }
467 }
468}