1use std::fmt;
16
17use crate::ast::{Bound, Decl, Def, ExceptPath, Expr, LetInstance, Module, Param, QuantKind, Unit};
18use crate::token::Op;
19
20const TRAILING: u8 = 0;
24const ATOM: u8 = u8::MAX;
25
26impl fmt::Display for Expr {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write(self, TRAILING, f)
29 }
30}
31
32impl fmt::Display for Bound {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 if self.destructure {
35 write!(f, "<<{}>>", self.names.join(", "))?;
36 } else {
37 f.write_str(&self.names.join(", "))?;
38 }
39 match &self.domain {
40 Some(domain) => write!(f, " \\in {domain}"),
41 None => Ok(()),
42 }
43 }
44}
45
46fn precedence(e: &Expr) -> u8 {
47 match e {
48 Expr::Binary(op, ..) => op.infix_prec().unwrap_or(TRAILING),
49 Expr::Unary(op, _) if op.is_postfix() => ATOM,
50 Expr::Unary(op, _) => op.prefix_prec().saturating_sub(1),
51 Expr::Quant { .. }
52 | Expr::Choose { .. }
53 | Expr::Let { .. }
54 | Expr::If { .. }
55 | Expr::Case { .. }
56 | Expr::Lambda { .. } => TRAILING,
57 _ => ATOM,
58 }
59}
60
61fn write(e: &Expr, min: u8, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 if precedence(e) < min {
63 write!(f, "(")?;
64 write(e, TRAILING, f)?;
65 return write!(f, ")");
66 }
67 bare(e, f)
68}
69
70#[expect(
71 clippy::too_many_lines,
72 reason = "one arm per syntactic form; splitting it would only scatter the grammar"
73)]
74fn bare(e: &Expr, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match e {
76 Expr::Num(n) => write!(f, "{n}"),
77 Expr::Decimal(text) => f.write_str(text),
78 Expr::Str(s) => write!(f, "\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")),
79 Expr::Bool(b) => f.write_str(if *b { "TRUE" } else { "FALSE" }),
80 Expr::Ident(name) => f.write_str(name),
81 Expr::At => f.write_str("@"),
82 Expr::Prime(inner) => {
83 write(inner, ATOM, f)?;
84 f.write_str("'")
85 }
86 Expr::Apply(head, args) => {
87 write(head, ATOM, f)?;
88 write!(f, "({})", list(args))
89 }
90 Expr::FnApply(head, args) => {
91 write(head, ATOM, f)?;
92 write!(f, "[{}]", list(args))
93 }
94 Expr::Field(inner, name) => {
95 write(inner, ATOM, f)?;
96 write!(f, ".{name}")
97 }
98 Expr::Qualified {
99 instance,
100 name,
101 args,
102 } => {
103 write!(f, "{instance}!{name}")?;
104 if args.is_empty() {
105 Ok(())
106 } else {
107 write!(f, "({})", list(args))
108 }
109 }
110 Expr::Unary(op, operand) if op.is_postfix() => {
111 write(operand, ATOM, f)?;
112 f.write_str(op.symbol())
113 }
114 Expr::Unary(op, operand) => {
115 f.write_str(op.symbol())?;
116 if op.is_word() {
117 f.write_str(" ")?;
118 }
119 write(operand, op.prefix_prec(), f)
120 }
121 Expr::Binary(op, lhs, rhs) => {
122 let prec = op.infix_prec().unwrap_or(TRAILING);
123 let (left, right) = if op.is_right_assoc() {
124 (prec + 1, prec)
125 } else {
126 (prec, prec + 1)
127 };
128 write(lhs, left, f)?;
129 if *op == Op::DotDot {
132 f.write_str(op.symbol())?;
133 } else {
134 write!(f, " {} ", op.symbol())?;
135 }
136 write(rhs, right, f)
137 }
138 Expr::Tuple(items) => write!(f, "<<{}>>", list(items)),
139 Expr::SetEnum(items) => write!(f, "{{{}}}", list(items)),
140 Expr::SetFilter { bound, pred } => write!(f, "{{{bound} : {pred}}}"),
141 Expr::SetMap { expr, bounds } => write!(f, "{{{expr} : {}}}", bounds_list(bounds)),
142 Expr::Record(fields) => write!(f, "[{}]", fields_list(fields, "|->")),
143 Expr::RecordSet(fields) => write!(f, "[{}]", fields_list(fields, ":")),
144 Expr::FnDef { bounds, body } => {
145 write!(f, "[{} |-> {body}]", bounds_list(bounds))
146 }
147 Expr::FnSet { domain, range } => write!(f, "[{domain} -> {range}]"),
148 Expr::Except { base, updates } => {
149 write!(f, "[{base} EXCEPT ")?;
150 for (i, (path, value)) in updates.iter().enumerate() {
151 if i > 0 {
152 f.write_str(", ")?;
153 }
154 f.write_str("!")?;
155 for step in path {
156 match step {
157 ExceptPath::Index(index) => write!(f, "[{index}]")?,
158 ExceptPath::Field(name) => write!(f, ".{name}")?,
159 }
160 }
161 write!(f, " = {value}")?;
162 }
163 f.write_str("]")
164 }
165 Expr::Quant { kind, bounds, body } => {
166 let symbol = match kind {
167 QuantKind::Forall => Op::Forall,
168 QuantKind::Exists => Op::Exists,
169 QuantKind::TemporalForall => Op::TemporalForall,
170 QuantKind::TemporalExists => Op::TemporalExists,
171 };
172 write!(f, "{} {} : {body}", symbol.symbol(), bounds_list(bounds))
173 }
174 Expr::Choose { bound, body } => write!(f, "CHOOSE {bound} : {body}"),
175 Expr::Let {
176 defs,
177 instances,
178 body,
179 } => {
180 f.write_str("LET ")?;
181 for (i, def) in defs.iter().enumerate() {
182 if i > 0 {
183 f.write_str(" ")?;
184 }
185 write!(f, "{def}")?;
186 }
187 for instance in instances {
188 if !defs.is_empty() {
189 f.write_str(" ")?;
190 }
191 write!(f, "{instance}")?;
192 }
193 write!(f, " IN {body}")
194 }
195 Expr::If {
196 cond,
197 then,
198 otherwise,
199 } => write!(f, "IF {cond} THEN {then} ELSE {otherwise}"),
200 Expr::Case { arms, other } => {
201 f.write_str("CASE ")?;
202 for (i, (guard, result)) in arms.iter().enumerate() {
203 if i > 0 {
204 f.write_str(" [] ")?;
205 }
206 write!(f, "{guard} -> {result}")?;
207 }
208 match other {
209 Some(value) => write!(f, " [] OTHER -> {value}"),
210 None => Ok(()),
211 }
212 }
213 Expr::Lambda { params, body } => write!(f, "LAMBDA {} : {body}", params_list(params)),
214 Expr::ActionBox { action, subscript } => write!(f, "[{action}]_{subscript}"),
215 Expr::ActionAngle { action, subscript } => write!(f, "<<{action}>>_{subscript}"),
216 Expr::Fairness {
217 strong,
218 subscript,
219 action,
220 } => {
221 let kind = if *strong { "SF" } else { "WF" };
222 write!(f, "{kind}_{subscript}({action})")
223 }
224 }
225}
226
227impl fmt::Display for Def {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 f.write_str(&self.name)?;
230 if !self.params.is_empty() {
231 write!(f, "({})", params_list(&self.params))?;
232 }
233 write!(f, " == {}", self.body)
234 }
235}
236
237impl fmt::Display for Param {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.write_str(&self.name)?;
240 if self.arity > 0 {
241 let holes = vec!["_"; self.arity].join(", ");
242 write!(f, "({holes})")?;
243 }
244 Ok(())
245 }
246}
247
248fn params_list(params: &[Param]) -> String {
249 params
250 .iter()
251 .map(ToString::to_string)
252 .collect::<Vec<_>>()
253 .join(", ")
254}
255
256fn list(items: &[Expr]) -> String {
257 items
258 .iter()
259 .map(ToString::to_string)
260 .collect::<Vec<_>>()
261 .join(", ")
262}
263
264fn bounds_list(bounds: &[Bound]) -> String {
265 bounds
266 .iter()
267 .map(ToString::to_string)
268 .collect::<Vec<_>>()
269 .join(", ")
270}
271
272fn fields_list(fields: &[(String, Expr)], separator: &str) -> String {
273 fields
274 .iter()
275 .map(|(name, value)| format!("{name} {separator} {value}"))
276 .collect::<Vec<_>>()
277 .join(", ")
278}
279
280impl fmt::Display for Module {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 writeln!(f, "---- MODULE {} ----", self.name)?;
289 if !self.extends.is_empty() {
290 writeln!(f, "EXTENDS {}", self.extends.join(", "))?;
291 }
292 for unit in &self.units {
293 write!(f, "{unit}")?;
294 }
295 writeln!(f, "====")
296 }
297}
298
299impl fmt::Display for Unit {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 match self {
302 Unit::Constants(decls) => writeln!(f, "CONSTANTS {}", decls_list(decls)),
303 Unit::Variables(names) => writeln!(f, "VARIABLES {}", names.join(", ")),
304 Unit::Recursive(decls) => writeln!(f, "RECURSIVE {}", decls_list(decls)),
305 Unit::Def(def) => {
306 if def.local {
307 write!(f, "LOCAL ")?;
308 }
309 writeln!(f, "{def}")
310 }
311 Unit::Instance { name, module, subs } => {
312 match name {
313 Some(name) => write!(f, "{name} == INSTANCE {module}")?,
314 None => write!(f, "INSTANCE {module}")?,
315 }
316 if !subs.is_empty() {
317 let with: Vec<String> = subs
318 .iter()
319 .map(|(name, value)| format!("{name} <- {value}"))
320 .collect();
321 write!(f, " WITH {}", with.join(", "))?;
322 }
323 writeln!(f)
324 }
325 Unit::Assume(e) => writeln!(f, "ASSUME {e}"),
326 Unit::Theorem(e) => writeln!(f, "THEOREM {e}"),
327 Unit::Inner(module) => write!(f, "{module}"),
328 Unit::Opaque => Ok(()),
329 }
330 }
331}
332
333impl fmt::Display for LetInstance {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 if let Some(name) = &self.name {
336 write!(f, "{name} == ")?;
337 }
338 write!(f, "INSTANCE {}", self.module)?;
339 if !self.subs.is_empty() {
340 let with: Vec<String> = self
341 .subs
342 .iter()
343 .map(|(name, value)| format!("{name} <- {value}"))
344 .collect();
345 write!(f, " WITH {}", with.join(", "))?;
346 }
347 Ok(())
348 }
349}
350
351impl fmt::Display for Decl {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 f.write_str(&self.name)?;
354 if self.arity > 0 {
355 write!(f, "({})", vec!["_"; self.arity].join(", "))?;
356 }
357 Ok(())
358 }
359}
360
361fn decls_list(decls: &[Decl]) -> String {
362 decls
363 .iter()
364 .map(ToString::to_string)
365 .collect::<Vec<_>>()
366 .join(", ")
367}