1use crate::ast::*;
14use crate::lexer::{Trivia, TriviaKind};
15
16pub fn render_from_ast(source: &str, module: &Module, trivia: &[Trivia]) -> Result<String, String> {
26 check_nesting(module)?;
27 tile(source, module, trivia)
28}
29
30fn check_nesting(module: &Module) -> Result<(), String> {
34 let mut spans: Vec<Span> = Vec::new();
35 collect_module(module, &mut spans);
36 for span in &spans {
37 if !span.is_valid() {
38 return Err(format!("invalid span [{}, {})", span.start, span.end));
39 }
40 }
41 spans.retain(|s| !s.is_empty());
42 spans.sort_by(|a, b| a.start.cmp(&b.start).then(b.end.cmp(&a.end)));
44
45 let mut stack: Vec<Span> = Vec::new();
46 for span in spans {
47 while stack.last().is_some_and(|top| top.end <= span.start) {
49 stack.pop();
50 }
51 if let Some(parent) = stack.last() {
55 if !parent.contains(&span) {
56 return Err(format!(
57 "span [{}, {}) overlaps [{}, {}) without nesting",
58 span.start, span.end, parent.start, parent.end
59 ));
60 }
61 }
62 stack.push(span);
63 }
64 Ok(())
65}
66
67fn tile(source: &str, module: &Module, trivia: &[Trivia]) -> Result<String, String> {
73 let mut content: Vec<Span> = Vec::new();
74 collect_module(module, &mut content);
75 let container = module.span;
76 content.retain(|s| !(s.is_empty() || (s.start == container.start && s.end == container.end)));
77
78 let mut items: Vec<(usize, usize)> = content.iter().map(|s| (s.start, s.end)).collect();
79 items.extend(
82 trivia
83 .iter()
84 .filter(|t| !matches!(t.kind, TriviaKind::BlankLines(_)))
85 .map(|t| (t.start, t.end)),
86 );
87 items.sort_unstable_by(|a, b| a.0.cmp(&b.0).then(b.1.cmp(&a.1)));
91
92 let mut out = String::with_capacity(source.len());
93 let mut prev = 0usize;
94 for (start, end) in items {
95 validate_interval(source, start, end)?;
96 if start < prev {
97 if end <= prev {
101 continue;
102 }
103 return Err(format!(
104 "span/trivia interval [{start}, {end}) overlaps previous tile ending at {prev}"
105 ));
106 }
107 let gap = &source[prev..start];
108 if !gap.chars().all(char::is_whitespace) {
109 return Err(format!(
110 "bytes {prev}..{start} not covered by any node or trivia span: {gap:?}"
111 ));
112 }
113 out.push_str(gap);
114 out.push_str(&source[start..end]);
115 prev = end;
116 }
117 let tail = &source[prev..];
118 if !tail.chars().all(char::is_whitespace) {
119 return Err(format!("bytes {prev}.. lost at EOF: {tail:?}"));
120 }
121 out.push_str(tail);
122
123 if out != source {
124 return Err(format!(
125 "reconstruction differs from source ({} vs {} bytes)",
126 out.len(),
127 source.len()
128 ));
129 }
130 Ok(out)
131}
132
133fn validate_interval(source: &str, start: usize, end: usize) -> Result<(), String> {
134 if start > end {
135 return Err(format!(
136 "span/trivia interval [{start}, {end}) has start after end"
137 ));
138 }
139 if end > source.len() {
140 return Err(format!(
141 "span/trivia interval [{start}, {end}) exceeds source length {}",
142 source.len()
143 ));
144 }
145 if !source.is_char_boundary(start) || !source.is_char_boundary(end) {
146 return Err(format!(
147 "span/trivia interval [{start}, {end}) does not align with UTF-8 boundaries"
148 ));
149 }
150 Ok(())
151}
152
153fn collect_module(m: &Module, out: &mut Vec<Span>) {
156 out.push(m.span);
157 if !m.header.is_empty() {
158 out.push(m.header);
159 }
160 for imp in &m.imports {
161 out.push(imp.span);
162 }
163 for d in &m.decls {
164 collect_decl(d, out);
165 }
166}
167
168fn collect_decl(d: &Decl, out: &mut Vec<Span>) {
169 match d {
170 Decl::Template(t) => {
171 out.push(t.span);
172 for f in &t.fields {
173 out.push(f.span);
174 }
175 for b in &t.body {
176 collect_tbody(b, out);
177 }
178 }
179 Decl::Interface(i) => {
180 out.push(i.span);
181 for m in &i.methods {
182 out.push(m.span);
183 }
184 for c in &i.choices {
185 collect_choice(c, out);
186 }
187 }
188 Decl::Function(f) => {
189 out.push(f.span);
192 for eq in &f.equations {
193 collect_eq(eq, out);
194 }
195 if let Some(sig) = f.sig_span {
196 out.push(sig);
197 }
198 }
199 Decl::TypeDef { span, .. } | Decl::Unknown { span, .. } => out.push(*span),
200 }
201}
202
203fn collect_tbody(b: &TemplateBodyDecl, out: &mut Vec<Span>) {
204 match b {
205 TemplateBodyDecl::Signatory { parties, span, .. }
206 | TemplateBodyDecl::Observer { parties, span, .. } => {
207 out.push(*span);
208 for e in parties {
209 collect_expr(e, out);
210 }
211 }
212 TemplateBodyDecl::Ensure { expr, span, .. }
213 | TemplateBodyDecl::Maintainer { expr, span, .. }
214 | TemplateBodyDecl::Key { expr, span, .. } => {
215 out.push(*span);
216 collect_expr(expr, out);
217 }
218 TemplateBodyDecl::Choice(c) => collect_choice(c, out),
219 TemplateBodyDecl::InterfaceInstance(ii) => {
220 out.push(ii.span);
221 for m in &ii.methods {
222 collect_binding(m, out);
223 }
224 }
225 TemplateBodyDecl::Other { span, .. } => out.push(*span),
226 }
227}
228
229fn collect_choice(c: &ChoiceDecl, out: &mut Vec<Span>) {
230 out.push(c.span);
231 for p in &c.params {
232 out.push(p.span);
233 }
234 for e in &c.controllers {
235 collect_expr(e, out);
236 }
237 for e in &c.observers {
238 collect_expr(e, out);
239 }
240 if let Some(b) = &c.body {
241 collect_expr(b, out);
242 }
243}
244
245fn collect_eq(eq: &Equation, out: &mut Vec<Span>) {
246 out.push(eq.span);
247 for p in &eq.params {
248 collect_pat(p, out);
249 }
250 collect_expr(&eq.body, out);
251 for (g, b) in &eq.guards {
252 collect_expr(g, out);
253 collect_expr(b, out);
254 }
255 for wb in &eq.where_bindings {
256 collect_binding(wb, out);
257 }
258}
259
260fn collect_binding(b: &Binding, out: &mut Vec<Span>) {
261 out.push(b.span);
262 collect_pat(&b.pat, out);
263 for p in &b.params {
264 collect_pat(p, out);
265 }
266 collect_expr(&b.expr, out);
267}
268
269fn collect_pat(p: &Pat, out: &mut Vec<Span>) {
270 out.push(p.span());
271 match p {
272 Pat::Con { args, .. } => {
273 for a in args {
274 collect_pat(a, out);
275 }
276 }
277 Pat::Tuple { items, .. } | Pat::List { items, .. } => {
278 for it in items {
279 collect_pat(it, out);
280 }
281 }
282 Pat::As { pat, .. } => collect_pat(pat, out),
283 _ => {}
284 }
285}
286
287fn collect_expr(e: &Expr, out: &mut Vec<Span>) {
288 out.push(e.span());
289 match e {
290 Expr::App { func, args, .. } => {
291 collect_expr(func, out);
292 for a in args {
293 collect_expr(a, out);
294 }
295 }
296 Expr::BinOp { lhs, rhs, .. } => {
297 collect_expr(lhs, out);
298 collect_expr(rhs, out);
299 }
300 Expr::Neg { expr, .. } => collect_expr(expr, out),
301 Expr::Lambda { params, body, .. } => {
302 for p in params {
303 collect_pat(p, out);
304 }
305 collect_expr(body, out);
306 }
307 Expr::If {
308 cond,
309 then_branch,
310 else_branch,
311 ..
312 } => {
313 collect_expr(cond, out);
314 collect_expr(then_branch, out);
315 collect_expr(else_branch, out);
316 }
317 Expr::Case {
318 scrutinee, alts, ..
319 } => {
320 collect_expr(scrutinee, out);
321 for a in alts {
322 collect_alt(a, out);
323 }
324 }
325 Expr::Do { stmts, .. } => {
326 for s in stmts {
327 collect_dostmt(s, out);
328 }
329 }
330 Expr::LetIn { bindings, body, .. } => {
331 for b in bindings {
332 collect_binding(b, out);
333 }
334 collect_expr(body, out);
335 }
336 Expr::Record { base, fields, .. } => {
337 collect_expr(base, out);
338 for f in fields {
339 collect_field_assign(f, out);
340 }
341 }
342 Expr::Tuple { items, .. } | Expr::List { items, .. } => {
343 for it in items {
344 collect_expr(it, out);
345 }
346 }
347 Expr::Try { body, handlers, .. } => {
348 collect_expr(body, out);
349 for h in handlers {
350 collect_alt(h, out);
351 }
352 }
353 Expr::Section {
354 operand: Some(o), ..
355 } => collect_expr(o, out),
356 _ => {}
357 }
358}
359
360fn collect_alt(a: &Alt, out: &mut Vec<Span>) {
361 out.push(a.span);
362 collect_pat(&a.pat, out);
363 collect_expr(&a.body, out);
364}
365
366fn collect_field_assign(f: &FieldAssign, out: &mut Vec<Span>) {
367 out.push(f.span);
368 if let Some(v) = &f.value {
369 collect_expr(v, out);
370 }
371}
372
373fn collect_dostmt(s: &DoStmt, out: &mut Vec<Span>) {
374 match s {
375 DoStmt::Bind {
376 pat, expr, span, ..
377 } => {
378 out.push(*span);
379 collect_pat(pat, out);
380 collect_expr(expr, out);
381 }
382 DoStmt::Let { bindings, span, .. } => {
383 out.push(*span);
384 for b in bindings {
385 collect_binding(b, out);
386 }
387 }
388 DoStmt::Expr { expr, span, .. } => {
389 out.push(*span);
390 collect_expr(expr, out);
391 }
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398 use crate::lexer::{Pos, Trivia};
399
400 #[test]
401 fn tile_reports_overlapping_intervals() {
402 let source = "module M where\n-- comment\n";
403 let module = Module {
404 name: "M".to_string(),
405 pos: Pos { line: 1, column: 1 },
406 span: Span::new(0, source.len()),
407 header: Span::new(0, "module M where".len()),
408 imports: Vec::new(),
409 decls: Vec::new(),
410 };
411 let trivia = vec![Trivia {
412 kind: TriviaKind::LineComment,
413 text: "-- comment".to_string(),
414 pos: Pos { line: 2, column: 1 },
415 start: "module M wher".len(),
416 end: "module M where\n-- comment".len(),
417 }];
418
419 let err = tile(source, &module, &trivia).unwrap_err();
420 assert!(
421 err.contains("overlaps previous tile"),
422 "overlap should fail loudly, got: {err}"
423 );
424 }
425}