1#![allow(unused_variables)] use crate::ast::*;
7
8const INDENT: &str = " ";
9
10pub fn format_program(p: &Program) -> String {
12 p.statements
13 .iter()
14 .map(|s| format_statement_indent(s, 0))
15 .collect::<Vec<_>>()
16 .join("\n")
17}
18
19pub(crate) fn format_sub_sig_param(p: &SubSigParam) -> String {
20 use crate::ast::MatchArrayElem;
21 match p {
22 SubSigParam::Scalar(name, ty, default) => {
23 let mut s = format!("${}", name);
24 if let Some(t) = ty {
25 s.push_str(": ");
26 s.push_str(&t.display_name());
27 }
28 if let Some(d) = default {
29 s.push_str(" = ");
30 s.push_str(&format_expr(d));
31 }
32 s
33 }
34 SubSigParam::Array(name, default) => {
35 let mut s = format!("@{}", name);
36 if let Some(d) = default {
37 s.push_str(" = ");
38 s.push_str(&format_expr(d));
39 }
40 s
41 }
42 SubSigParam::Hash(name, default) => {
43 let mut s = format!("%{}", name);
44 if let Some(d) = default {
45 s.push_str(" = ");
46 s.push_str(&format_expr(d));
47 }
48 s
49 }
50 SubSigParam::ArrayDestruct(elems) => {
51 let inner = elems
52 .iter()
53 .map(|x| match x {
54 MatchArrayElem::Expr(e) => format_expr(e),
55 MatchArrayElem::CaptureScalar(name) => format!("${}", name),
56 MatchArrayElem::Rest => "*".to_string(),
57 MatchArrayElem::RestBind(name) => format!("@{}", name),
58 })
59 .collect::<Vec<_>>()
60 .join(", ");
61 format!("[{inner}]")
62 }
63 SubSigParam::HashDestruct(pairs) => {
64 let inner = pairs
65 .iter()
66 .map(|(k, v)| format!("{} => ${}", k, v))
67 .collect::<Vec<_>>()
68 .join(", ");
69 format!("{{ {inner} }}")
70 }
71 }
72}
73
74#[allow(dead_code)]
75fn format_statement(s: &Statement) -> String {
76 format_statement_indent(s, 0)
77}
78
79fn format_statement_indent(s: &Statement, depth: usize) -> String {
80 let prefix = INDENT.repeat(depth);
81 let lab = s
82 .label
83 .as_ref()
84 .map(|l| format!("{}: ", l))
85 .unwrap_or_default();
86 let body = match &s.kind {
87 StmtKind::Expression(e) => format_expr(e),
88 StmtKind::If {
89 condition,
90 body,
91 elsifs,
92 else_block,
93 } => {
94 let mut s = format!(
95 "if ({}) {{\n{}\n{}}}",
96 format_expr(condition),
97 format_block_indent(body, depth + 1),
98 prefix
99 );
100 for (c, b) in elsifs {
101 s.push_str(&format!(
102 " elsif ({}) {{\n{}\n{}}}",
103 format_expr(c),
104 format_block_indent(b, depth + 1),
105 prefix
106 ));
107 }
108 if let Some(eb) = else_block {
109 s.push_str(&format!(
110 " else {{\n{}\n{}}}",
111 format_block_indent(eb, depth + 1),
112 prefix
113 ));
114 }
115 s
116 }
117 StmtKind::Unless {
118 condition,
119 body,
120 else_block,
121 } => {
122 let mut s = format!(
123 "unless ({}) {{\n{}\n{}}}",
124 format_expr(condition),
125 format_block_indent(body, depth + 1),
126 prefix
127 );
128 if let Some(eb) = else_block {
129 s.push_str(&format!(
130 " else {{\n{}\n{}}}",
131 format_block_indent(eb, depth + 1),
132 prefix
133 ));
134 }
135 s
136 }
137 StmtKind::While {
138 condition,
139 body,
140 label,
141 continue_block,
142 } => {
143 let lb = label
144 .as_ref()
145 .map(|l| format!("{}: ", l))
146 .unwrap_or_default();
147 let mut s = format!(
148 "{}while ({}) {{\n{}\n{}}}",
149 lb,
150 format_expr(condition),
151 format_block_indent(body, depth + 1),
152 prefix
153 );
154 if let Some(cb) = continue_block {
155 s.push_str(&format!(
156 " continue {{\n{}\n{}}}",
157 format_block_indent(cb, depth + 1),
158 prefix
159 ));
160 }
161 s
162 }
163 StmtKind::Until {
164 condition,
165 body,
166 label,
167 continue_block,
168 } => {
169 let lb = label
170 .as_ref()
171 .map(|l| format!("{}: ", l))
172 .unwrap_or_default();
173 let mut s = format!(
174 "{}until ({}) {{\n{}\n{}}}",
175 lb,
176 format_expr(condition),
177 format_block_indent(body, depth + 1),
178 prefix
179 );
180 if let Some(cb) = continue_block {
181 s.push_str(&format!(
182 " continue {{\n{}\n{}}}",
183 format_block_indent(cb, depth + 1),
184 prefix
185 ));
186 }
187 s
188 }
189 StmtKind::DoWhile { body, condition } => {
190 format!(
191 "do {{\n{}\n{}}} while ({})",
192 format_block_indent(body, depth + 1),
193 prefix,
194 format_expr(condition)
195 )
196 }
197 StmtKind::For {
198 init,
199 condition,
200 step,
201 body,
202 label,
203 continue_block,
204 } => {
205 let lb = label
206 .as_ref()
207 .map(|l| format!("{}: ", l))
208 .unwrap_or_default();
209 let ini = init
210 .as_ref()
211 .map(|s| format_statement_indent(s, 0))
212 .unwrap_or_default();
213 let cond = condition.as_ref().map(format_expr).unwrap_or_default();
214 let st = step.as_ref().map(format_expr).unwrap_or_default();
215 let mut s = format!(
216 "{}for ({}; {}; {}) {{\n{}\n{}}}",
217 lb,
218 ini,
219 cond,
220 st,
221 format_block_indent(body, depth + 1),
222 prefix
223 );
224 if let Some(cb) = continue_block {
225 s.push_str(&format!(
226 " continue {{\n{}\n{}}}",
227 format_block_indent(cb, depth + 1),
228 prefix
229 ));
230 }
231 s
232 }
233 StmtKind::Foreach {
234 var,
235 list,
236 body,
237 label,
238 continue_block,
239 } => {
240 let lb = label
241 .as_ref()
242 .map(|l| format!("{}: ", l))
243 .unwrap_or_default();
244 let mut s = format!(
245 "{}for ${} ({}) {{\n{}\n{}}}",
246 lb,
247 var,
248 format_expr(list),
249 format_block_indent(body, depth + 1),
250 prefix
251 );
252 if let Some(cb) = continue_block {
253 s.push_str(&format!(
254 " continue {{\n{}\n{}}}",
255 format_block_indent(cb, depth + 1),
256 prefix
257 ));
258 }
259 s
260 }
261 StmtKind::SubDecl {
262 name,
263 params,
264 body,
265 prototype,
266 } => {
267 let sig = if !params.is_empty() {
268 format!(
269 " ({})",
270 params
271 .iter()
272 .map(format_sub_sig_param)
273 .collect::<Vec<_>>()
274 .join(", ")
275 )
276 } else {
277 prototype
278 .as_ref()
279 .map(|p| format!(" ({})", p))
280 .unwrap_or_default()
281 };
282 format!(
283 "fn {}{} {{\n{}\n{}}}",
284 name,
285 sig,
286 format_block_indent(body, depth + 1),
287 prefix
288 )
289 }
290 StmtKind::Package { name } => format!("package {}", name),
291 StmtKind::UsePerlVersion { version } => {
292 if version.fract() == 0.0 && *version >= 0.0 {
293 format!("use {}", *version as i64)
294 } else {
295 format!("use {}", version)
296 }
297 }
298 StmtKind::Use { module, imports } => {
299 if imports.is_empty() {
300 format!("use {}", module)
301 } else {
302 format!("use {} {}", module, format_expr_list(imports))
303 }
304 }
305 StmtKind::UseOverload { pairs } => {
306 let inner = pairs
307 .iter()
308 .map(|(k, v)| {
309 format!(
310 "'{}' => '{}'",
311 k.replace('\'', "\\'"),
312 v.replace('\'', "\\'")
313 )
314 })
315 .collect::<Vec<_>>()
316 .join(", ");
317 format!("use overload {inner}")
318 }
319 StmtKind::No { module, imports } => {
320 if imports.is_empty() {
321 format!("no {}", module)
322 } else {
323 format!("no {} {}", module, format_expr_list(imports))
324 }
325 }
326 StmtKind::Return(e) => e
327 .as_ref()
328 .map(|x| format!("return {}", format_expr(x)))
329 .unwrap_or_else(|| "return".to_string()),
330 StmtKind::Last(l) => l
331 .as_ref()
332 .map(|x| format!("last {}", x))
333 .unwrap_or_else(|| "last".to_string()),
334 StmtKind::Next(l) => l
335 .as_ref()
336 .map(|x| format!("next {}", x))
337 .unwrap_or_else(|| "next".to_string()),
338 StmtKind::Redo(l) => l
339 .as_ref()
340 .map(|x| format!("redo {}", x))
341 .unwrap_or_else(|| "redo".to_string()),
342 StmtKind::My(decls) => format!("my {}", format_var_decls(decls)),
343 StmtKind::Our(decls) => format!("our {}", format_var_decls(decls)),
344 StmtKind::Local(decls) => format!("local {}", format_var_decls(decls)),
345 StmtKind::State(decls) => format!("state {}", format_var_decls(decls)),
346 StmtKind::LocalExpr {
347 target,
348 initializer,
349 } => {
350 let mut s = format!("local {}", format_expr(target));
351 if let Some(init) = initializer {
352 s.push_str(&format!(" = {}", format_expr(init)));
353 }
354 s
355 }
356 StmtKind::MySync(decls) => format!("mysync {}", format_var_decls(decls)),
357 StmtKind::OurSync(decls) => format!("oursync {}", format_var_decls(decls)),
358 StmtKind::StmtGroup(b) => format_block_indent(b, depth),
359 StmtKind::Block(b) => format!("{{\n{}\n{}}}", format_block_indent(b, depth + 1), prefix),
360 StmtKind::Begin(b) => format!(
361 "BEGIN {{\n{}\n{}}}",
362 format_block_indent(b, depth + 1),
363 prefix
364 ),
365 StmtKind::UnitCheck(b) => format!(
366 "UNITCHECK {{\n{}\n{}}}",
367 format_block_indent(b, depth + 1),
368 prefix
369 ),
370 StmtKind::Check(b) => format!(
371 "CHECK {{\n{}\n{}}}",
372 format_block_indent(b, depth + 1),
373 prefix
374 ),
375 StmtKind::Init(b) => format!(
376 "INIT {{\n{}\n{}}}",
377 format_block_indent(b, depth + 1),
378 prefix
379 ),
380 StmtKind::End(b) => format!(
381 "END {{\n{}\n{}}}",
382 format_block_indent(b, depth + 1),
383 prefix
384 ),
385 StmtKind::Empty => String::new(),
386 StmtKind::Goto { target } => format!("goto {}", format_expr(target)),
387 StmtKind::Continue(b) => format!(
388 "continue {{\n{}\n{}}}",
389 format_block_indent(b, depth + 1),
390 prefix
391 ),
392 StmtKind::StructDecl { def } => {
393 let fields = def
394 .fields
395 .iter()
396 .map(|f| format!("{} => {}", f.name, f.ty.display_name()))
397 .collect::<Vec<_>>()
398 .join(", ");
399 format!("struct {} {{ {} }}", def.name, fields)
400 }
401 StmtKind::EnumDecl { def } => {
402 let variants = def
403 .variants
404 .iter()
405 .map(|v| {
406 if let Some(ty) = &v.ty {
407 format!("{} => {}", v.name, ty.display_name())
408 } else {
409 v.name.clone()
410 }
411 })
412 .collect::<Vec<_>>()
413 .join(", ");
414 format!("enum {} {{ {} }}", def.name, variants)
415 }
416 StmtKind::ClassDecl { def } => {
417 let prefix = if def.is_abstract {
418 "abstract "
419 } else if def.is_final {
420 "final "
421 } else {
422 ""
423 };
424 let mut header = format!("{}class {}", prefix, def.name);
425 if !def.extends.is_empty() {
426 header.push_str(&format!(" extends {}", def.extends.join(", ")));
427 }
428 if !def.implements.is_empty() {
429 header.push_str(&format!(" impl {}", def.implements.join(", ")));
430 }
431 let fields = def
432 .fields
433 .iter()
434 .map(|f| {
435 let vis = match f.visibility {
436 crate::ast::Visibility::Private => "priv ",
437 crate::ast::Visibility::Protected => "prot ",
438 crate::ast::Visibility::Public => "",
439 };
440 format!("{}{}: {}", vis, f.name, f.ty.display_name())
441 })
442 .collect::<Vec<_>>()
443 .join("; ");
444 format!("{} {{ {} }}", header, fields)
445 }
446 StmtKind::TraitDecl { def } => {
447 let methods = def
448 .methods
449 .iter()
450 .map(|m| format!("fn {}", m.name))
451 .collect::<Vec<_>>()
452 .join("; ");
453 format!("trait {} {{ {} }}", def.name, methods)
454 }
455 StmtKind::EvalTimeout { timeout, body } => {
456 format!(
457 "eval_timeout {} {{\n{}\n{}}}",
458 format_expr(timeout),
459 format_block_indent(body, depth + 1),
460 prefix
461 )
462 }
463 StmtKind::TryCatch {
464 try_block,
465 catch_var,
466 catch_block,
467 finally_block,
468 } => {
469 let fin = finally_block
470 .as_ref()
471 .map(|b| {
472 format!(
473 " finally {{\n{}\n{}}}",
474 format_block_indent(b, depth + 1),
475 prefix
476 )
477 })
478 .unwrap_or_default();
479 format!(
480 "try {{\n{}\n{}}} catch (${}) {{\n{}\n{}}}{}",
481 format_block_indent(try_block, depth + 1),
482 prefix,
483 catch_var,
484 format_block_indent(catch_block, depth + 1),
485 prefix,
486 fin
487 )
488 }
489 StmtKind::Given { topic, body } => {
490 format!(
491 "given ({}) {{\n{}\n{}}}",
492 format_expr(topic),
493 format_block_indent(body, depth + 1),
494 prefix
495 )
496 }
497 StmtKind::When { cond, body } => {
498 format!(
499 "when ({}) {{\n{}\n{}}}",
500 format_expr(cond),
501 format_block_indent(body, depth + 1),
502 prefix
503 )
504 }
505 StmtKind::DefaultCase { body } => format!(
506 "default {{\n{}\n{}}}",
507 format_block_indent(body, depth + 1),
508 prefix
509 ),
510 StmtKind::FormatDecl { name, lines } => {
511 let mut s = format!("format {} =\n", name);
512 for ln in lines {
513 s.push_str(ln);
514 s.push('\n');
515 }
516 s.push('.');
517 s
518 }
519 StmtKind::Tie {
520 target,
521 class,
522 args,
523 } => {
524 let target_s = match target {
525 crate::ast::TieTarget::Hash(h) => format!("%{}", h),
526 crate::ast::TieTarget::Array(a) => format!("@{}", a),
527 crate::ast::TieTarget::Scalar(s) => format!("${}", s),
528 };
529 let mut s = format!("tie {} {}", target_s, format_expr(class));
530 for a in args {
531 s.push_str(&format!(", {}", format_expr(a)));
532 }
533 s
534 }
535 StmtKind::AdviceDecl {
536 kind,
537 pattern,
538 body,
539 } => {
540 let kw = match kind {
541 crate::ast::AdviceKind::Before => "before",
542 crate::ast::AdviceKind::After => "after",
543 crate::ast::AdviceKind::Around => "around",
544 };
545 format!(
546 "{} \"{}\" {{\n{}\n{}}}",
547 kw,
548 pattern,
549 format_block_indent(body, depth + 1),
550 prefix
551 )
552 }
553 };
554 format!("{}{}{}", prefix, lab, body)
555}
556
557pub fn format_block(b: &Block) -> String {
558 format_block_indent(b, 0)
559}
560
561fn format_block_indent(b: &Block, depth: usize) -> String {
562 b.iter()
563 .map(|s| format_statement_indent(s, depth))
564 .collect::<Vec<_>>()
565 .join("\n")
566}
567
568fn format_block_inline(b: &Block) -> String {
570 b.iter()
571 .map(|s| format_statement_indent(s, 0))
572 .collect::<Vec<_>>()
573 .join("; ")
574}
575
576fn format_var_decls(decls: &[VarDecl]) -> String {
577 decls
578 .iter()
579 .map(|d| {
580 let sig = match d.sigil {
581 Sigil::Scalar => "$",
582 Sigil::Array => "@",
583 Sigil::Hash => "%",
584 Sigil::Typeglob => "*",
585 };
586 let mut s = format!("{}{}", sig, d.name);
587 if let Some(ref t) = d.type_annotation {
588 s.push_str(&format!(" : {}", t.display_name()));
589 }
590 if let Some(ref init) = d.initializer {
591 s.push_str(&format!(" = {}", format_expr(init)));
592 }
593 s
594 })
595 .collect::<Vec<_>>()
596 .join(", ")
597}
598
599pub(crate) fn format_expr_list(es: &[Expr]) -> String {
600 es.iter().map(format_expr).collect::<Vec<_>>().join(", ")
601}
602
603pub(crate) fn format_binop(op: BinOp) -> &'static str {
604 match op {
605 BinOp::Add => "+",
606 BinOp::Sub => "-",
607 BinOp::Mul => "*",
608 BinOp::Div => "/",
609 BinOp::Mod => "%",
610 BinOp::Pow => "**",
611 BinOp::Concat => ".",
612 BinOp::NumEq => "==",
613 BinOp::NumNe => "!=",
614 BinOp::NumLt => "<",
615 BinOp::NumGt => ">",
616 BinOp::NumLe => "<=",
617 BinOp::NumGe => ">=",
618 BinOp::Spaceship => "<=>",
619 BinOp::StrEq => "eq",
620 BinOp::StrNe => "ne",
621 BinOp::StrLt => "lt",
622 BinOp::StrGt => "gt",
623 BinOp::StrLe => "le",
624 BinOp::StrGe => "ge",
625 BinOp::StrCmp => "cmp",
626 BinOp::LogAnd => "&&",
627 BinOp::LogOr => "||",
628 BinOp::DefinedOr => "//",
629 BinOp::BitAnd => "&",
630 BinOp::BitOr => "|",
631 BinOp::BitXor => "^",
632 BinOp::ShiftLeft => "<<",
633 BinOp::ShiftRight => ">>",
634 BinOp::LogAndWord => "and",
635 BinOp::LogOrWord => "or",
636 BinOp::BindMatch => "=~",
637 BinOp::BindNotMatch => "!~",
638 }
639}
640
641pub(crate) fn format_unary(op: UnaryOp) -> &'static str {
642 match op {
643 UnaryOp::Negate => "-",
644 UnaryOp::LogNot => "!",
645 UnaryOp::BitNot => "~",
646 UnaryOp::LogNotWord => "not",
647 UnaryOp::PreIncrement => "++",
648 UnaryOp::PreDecrement => "--",
649 UnaryOp::Ref => "\\",
650 }
651}
652
653pub(crate) fn format_postfix(op: PostfixOp) -> &'static str {
654 match op {
655 PostfixOp::Increment => "++",
656 PostfixOp::Decrement => "--",
657 }
658}
659
660pub(crate) fn format_string_part(p: &StringPart) -> String {
661 match p {
662 StringPart::Literal(s) => escape_interpolated_literal(s),
663 StringPart::ScalarVar(n) => format!("${{{}}}", n),
664 StringPart::ArrayVar(n) => format!("@{{{}}}", n),
665 StringPart::Expr(e) => format_expr(e),
666 }
667}
668
669pub(crate) fn escape_interpolated_literal(s: &str) -> String {
671 let mut out = String::new();
672 for c in s.chars() {
673 match c {
674 '\\' => out.push_str("\\\\"),
675 '"' => out.push_str("\\\""),
676 '\n' => out.push_str("\\n"),
677 '\r' => out.push_str("\\r"),
678 '\t' => out.push_str("\\t"),
679 '\x1b' => out.push_str("\\e"),
680 c if c.is_control() => {
681 out.push_str(&format!("\\x{{{:02x}}}", c as u32));
682 }
683 _ => out.push(c),
684 }
685 }
686 out
687}
688
689pub(crate) fn escape_regex_part(s: &str) -> String {
692 let mut out = String::new();
693 for c in s.chars() {
694 match c {
695 '\n' => out.push_str("\\n"),
696 '\r' => out.push_str("\\r"),
697 '\t' => out.push_str("\\t"),
698 '\x1b' => out.push_str("\\x1b"),
699 c if c.is_control() => {
700 out.push_str(&format!("\\x{:02x}", c as u32));
701 }
702 _ => out.push(c),
703 }
704 }
705 out
706}
707
708pub(crate) fn format_string_literal(s: &str) -> String {
709 let mut out = String::new();
710 out.push('"');
711 for c in s.chars() {
712 match c {
713 '\\' => out.push_str("\\\\"),
714 '"' => out.push_str("\\\""),
715 '\n' => out.push_str("\\n"),
716 '\r' => out.push_str("\\r"),
717 '\t' => out.push_str("\\t"),
718 '\x1b' => out.push_str("\\e"),
719 c if c.is_control() => {
720 out.push_str(&format!("\\x{{{:02x}}}", c as u32));
721 }
722 _ => out.push(c),
723 }
724 }
725 out.push('"');
726 out
727}
728
729pub fn format_expr(e: &Expr) -> String {
731 match &e.kind {
732 ExprKind::Integer(n) => n.to_string(),
733 ExprKind::Float(f) => format!("{}", f),
734 ExprKind::String(s) => format_string_literal(s),
735 ExprKind::Bareword(s) => s.clone(),
736 ExprKind::Regex(p, fl) => format!("/{}/{}/", p, fl),
737 ExprKind::QW(ws) => format!("qw({})", ws.join(" ")),
738 ExprKind::Undef => "undef".to_string(),
739 ExprKind::MagicConst(crate::ast::MagicConstKind::File) => "__FILE__".to_string(),
740 ExprKind::MagicConst(crate::ast::MagicConstKind::Line) => "__LINE__".to_string(),
741 ExprKind::MagicConst(crate::ast::MagicConstKind::Sub) => "__SUB__".to_string(),
742 ExprKind::InterpolatedString(parts) => {
743 format!(
744 "\"{}\"",
745 parts.iter().map(format_string_part).collect::<String>()
746 )
747 }
748 ExprKind::ScalarVar(name) => format!("${}", name),
749 ExprKind::ArrayVar(name) => format!("@{}", name),
750 ExprKind::HashVar(name) => format!("%{}", name),
751 ExprKind::Typeglob(name) => format!("*{}", name),
752 ExprKind::TypeglobExpr(e) => format!("*{{ {} }}", format_expr(e)),
753 ExprKind::ArrayElement { array, index } => format!("${}[{}]", array, format_expr(index)),
754 ExprKind::HashElement { hash, key } => format!("${}{{{}}}", hash, format_expr(key)),
755 ExprKind::ArraySlice { array, indices } => format!(
756 "@{}[{}]",
757 array,
758 indices
759 .iter()
760 .map(format_expr)
761 .collect::<Vec<_>>()
762 .join(", ")
763 ),
764 ExprKind::HashSlice { hash, keys } => format!(
765 "@{}{{{}}}",
766 hash,
767 keys.iter().map(format_expr).collect::<Vec<_>>().join(", ")
768 ),
769 ExprKind::HashKvSlice { hash, keys } => format!(
770 "%{}{{{}}}",
771 hash,
772 keys.iter().map(format_expr).collect::<Vec<_>>().join(", ")
773 ),
774 ExprKind::HashSliceDeref { container, keys } => format!(
775 "@{}{{{}}}",
776 format_expr(container),
777 keys.iter().map(format_expr).collect::<Vec<_>>().join(", ")
778 ),
779 ExprKind::AnonymousListSlice { source, indices } => format!(
780 "({})[{}]",
781 format_expr(source),
782 indices
783 .iter()
784 .map(format_expr)
785 .collect::<Vec<_>>()
786 .join(", ")
787 ),
788 ExprKind::ScalarRef(inner) => format!("\\{}", format_expr(inner)),
789 ExprKind::ArrayRef(elems) => format!("[{}]", format_expr_list(elems)),
790 ExprKind::HashRef(pairs) => {
791 let inner = pairs
792 .iter()
793 .map(|(k, v)| format!("{} => {}", format_expr(k), format_expr(v)))
794 .collect::<Vec<_>>()
795 .join(", ");
796 format!("{{{}}}", inner)
797 }
798 ExprKind::CodeRef { params, body } => {
799 if params.is_empty() {
800 format!("fn {{ {} }}", format_block_inline(body))
801 } else {
802 let sig = params
803 .iter()
804 .map(format_sub_sig_param)
805 .collect::<Vec<_>>()
806 .join(", ");
807 format!("fn ({}) {{ {} }}", sig, format_block_inline(body))
808 }
809 }
810 ExprKind::SubroutineRef(name) => format!("&{}", name),
811 ExprKind::SubroutineCodeRef(name) => format!("\\&{}", name),
812 ExprKind::DynamicSubCodeRef(e) => format!("\\&{{ {} }}", format_expr(e)),
813 ExprKind::Deref { expr, kind } => match kind {
814 Sigil::Scalar => format!("${{{}}}", format_expr(expr)),
815 Sigil::Array => format!("@{{${}}}", format_expr(expr)),
816 Sigil::Hash => format!("%{{${}}}", format_expr(expr)),
817 Sigil::Typeglob => format!("*{{${}}}", format_expr(expr)),
818 },
819 ExprKind::ArrowDeref { expr, index, kind } => match kind {
820 DerefKind::Array => format!("({})->[{}]", format_expr(expr), format_expr(index)),
821 DerefKind::Hash => format!("({})->{{{}}}", format_expr(expr), format_expr(index)),
822 DerefKind::Call => format!("({})->({})", format_expr(expr), format_expr(index)),
823 },
824 ExprKind::BinOp { left, op, right } => format!(
825 "{} {} {}",
826 format_expr(left),
827 format_binop(*op),
828 format_expr(right)
829 ),
830 ExprKind::UnaryOp { op, expr } => format!("{}{}", format_unary(*op), format_expr(expr)),
831 ExprKind::PostfixOp { expr, op } => {
832 format!("{}{}", format_expr(expr), format_postfix(*op))
833 }
834 ExprKind::Assign { target, value } => {
835 format!("{} = {}", format_expr(target), format_expr(value))
836 }
837 ExprKind::CompoundAssign { target, op, value } => format!(
838 "{} {}= {}",
839 format_expr(target),
840 format_binop(*op),
841 format_expr(value)
842 ),
843 ExprKind::Ternary {
844 condition,
845 then_expr,
846 else_expr,
847 } => format!(
848 "{} ? {} : {}",
849 format_expr(condition),
850 format_expr(then_expr),
851 format_expr(else_expr)
852 ),
853 ExprKind::Repeat {
854 expr,
855 count,
856 list_repeat,
857 } => {
858 if *list_repeat && !matches!(expr.kind, ExprKind::List(_) | ExprKind::QW(_)) {
859 format!("({}) x {}", format_expr(expr), format_expr(count))
860 } else {
861 format!("{} x {}", format_expr(expr), format_expr(count))
862 }
863 }
864 ExprKind::Range {
865 from,
866 to,
867 exclusive,
868 step,
869 } => {
870 let op = if *exclusive { "..." } else { ".." };
871 if let Some(s) = step {
872 format!(
873 "{} {} {}:{}",
874 format_expr(from),
875 op,
876 format_expr(to),
877 format_expr(s)
878 )
879 } else {
880 format!("{} {} {}", format_expr(from), op, format_expr(to))
881 }
882 }
883 ExprKind::SliceRange { from, to, step } => {
884 let f = from.as_ref().map(|e| format_expr(e)).unwrap_or_default();
885 let t = to.as_ref().map(|e| format_expr(e)).unwrap_or_default();
886 match step {
887 Some(s) => format!("{}:{}:{}", f, t, format_expr(s)),
888 None => format!("{}:{}", f, t),
889 }
890 }
891 ExprKind::FuncCall { name, args } => format!(
892 "{}({})",
893 name,
894 args.iter().map(format_expr).collect::<Vec<_>>().join(", ")
895 ),
896 ExprKind::MethodCall {
897 object,
898 method,
899 args,
900 super_call,
901 } => {
902 let m = if *super_call {
903 format!("SUPER::{}", method)
904 } else {
905 method.clone()
906 };
907 format!(
908 "{}->{}({})",
909 format_expr(object),
910 m,
911 args.iter().map(format_expr).collect::<Vec<_>>().join(", ")
912 )
913 }
914 ExprKind::IndirectCall {
915 target,
916 args,
917 ampersand,
918 pass_caller_arglist,
919 } => {
920 if *pass_caller_arglist && args.is_empty() {
921 format!("&{}", format_expr(target))
922 } else {
923 let inner = format!(
924 "{}({})",
925 format_expr(target),
926 args.iter().map(format_expr).collect::<Vec<_>>().join(", ")
927 );
928 if *ampersand {
929 format!("&{}", inner)
930 } else {
931 inner
932 }
933 }
934 }
935 ExprKind::Print { handle, args } => {
936 let h = handle
937 .as_ref()
938 .map(|h| format!("{} ", h))
939 .unwrap_or_default();
940 format!("print {}{}", h, format_expr_list(args))
941 }
942 ExprKind::Say { handle, args } => {
943 let h = handle
944 .as_ref()
945 .map(|h| format!("{} ", h))
946 .unwrap_or_default();
947 format!("say {}{}", h, format_expr_list(args))
948 }
949 ExprKind::Printf { handle, args } => {
950 let h = handle
951 .as_ref()
952 .map(|h| format!("{} ", h))
953 .unwrap_or_default();
954 format!("printf {}{}", h, format_expr_list(args))
955 }
956 ExprKind::Die(args) => {
957 if args.is_empty() {
958 "die".to_string()
959 } else {
960 format!("die {}", format_expr_list(args))
961 }
962 }
963 ExprKind::Warn(args) => {
964 if args.is_empty() {
965 "warn".to_string()
966 } else {
967 format!("warn {}", format_expr_list(args))
968 }
969 }
970 ExprKind::Match {
971 expr,
972 pattern,
973 flags,
974 scalar_g: _,
975 delim: _,
976 } => format!("{} =~ /{}/{}", format_expr(expr), pattern, flags),
977 ExprKind::Substitution {
978 expr,
979 pattern,
980 replacement,
981 flags,
982 delim: _,
983 } => format!(
984 "{} =~ s/{}/{}/{}",
985 format_expr(expr),
986 pattern,
987 replacement,
988 flags
989 ),
990 ExprKind::Transliterate {
991 expr,
992 from,
993 to,
994 flags,
995 delim: _,
996 } => format!("{} =~ tr/{}/{}/{}", format_expr(expr), from, to, flags),
997 ExprKind::MapExpr {
998 block,
999 list,
1000 flatten_array_refs,
1001 stream,
1002 } => {
1003 let kw = match (*flatten_array_refs, *stream) {
1004 (true, true) => "flat_maps",
1005 (true, false) => "flat_map",
1006 (false, true) => "maps",
1007 (false, false) => "map",
1008 };
1009 format!(
1010 "{kw} {{ {} }} {}",
1011 format_block_inline(block),
1012 format_expr(list)
1013 )
1014 }
1015 ExprKind::MapExprComma {
1016 expr,
1017 list,
1018 flatten_array_refs,
1019 stream,
1020 } => {
1021 let kw = match (*flatten_array_refs, *stream) {
1022 (true, true) => "flat_maps",
1023 (true, false) => "flat_map",
1024 (false, true) => "maps",
1025 (false, false) => "map",
1026 };
1027 format!("{kw} {}, {}", format_expr(expr), format_expr(list))
1028 }
1029 ExprKind::GrepExpr {
1030 block,
1031 list,
1032 keyword,
1033 } => {
1034 format!(
1035 "{} {{ {} }} {}",
1036 keyword.as_str(),
1037 format_block_inline(block),
1038 format_expr(list)
1039 )
1040 }
1041 ExprKind::GrepExprComma {
1042 expr,
1043 list,
1044 keyword,
1045 } => {
1046 format!(
1047 "{} {}, {}",
1048 keyword.as_str(),
1049 format_expr(expr),
1050 format_expr(list)
1051 )
1052 }
1053 ExprKind::ForEachExpr { block, list } => {
1054 format!(
1055 "fore {{ {} }} {}",
1056 format_block_inline(block),
1057 format_expr(list)
1058 )
1059 }
1060 ExprKind::SortExpr { cmp, list } => match cmp {
1061 Some(crate::ast::SortComparator::Block(b)) => {
1062 format!(
1063 "sort {{ {} }} {}",
1064 format_block_inline(b),
1065 format_expr(list)
1066 )
1067 }
1068 Some(crate::ast::SortComparator::Code(e)) => {
1069 format!("sort {} {}", format_expr(e), format_expr(list))
1070 }
1071 None => format!("sort {}", format_expr(list)),
1072 },
1073 ExprKind::ReverseExpr(e) => format!("reverse {}", format_expr(e)),
1074 ExprKind::Rev(e) => format!("rev {}", format_expr(e)),
1075 ExprKind::JoinExpr { separator, list } => {
1076 format!("join({}, {})", format_expr(separator), format_expr(list))
1077 }
1078 ExprKind::SplitExpr {
1079 pattern,
1080 string,
1081 limit,
1082 } => match limit {
1083 Some(l) => format!(
1084 "split({}, {}, {})",
1085 format_expr(pattern),
1086 format_expr(string),
1087 format_expr(l)
1088 ),
1089 None => format!("split({}, {})", format_expr(pattern), format_expr(string)),
1090 },
1091 ExprKind::PMapExpr {
1092 block,
1093 list,
1094 progress,
1095 flat_outputs,
1096 on_cluster,
1097 stream: _,
1098 } => {
1099 let kw = match (flat_outputs, on_cluster.is_some()) {
1100 (true, true) => "pflat_map_on",
1101 (true, false) => "pflat_map",
1102 (false, true) => "pmap_on",
1103 (false, false) => "pmap",
1104 };
1105 let base = if let Some(c) = on_cluster {
1106 format!(
1107 "{kw} {} {{ {} }} {}",
1108 format_expr(c),
1109 format_block_inline(block),
1110 format_expr(list)
1111 )
1112 } else {
1113 format!(
1114 "{kw} {{ {} }} {}",
1115 format_block_inline(block),
1116 format_expr(list)
1117 )
1118 };
1119 match progress {
1120 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1121 None => base,
1122 }
1123 }
1124 ExprKind::PMapChunkedExpr {
1125 chunk_size,
1126 block,
1127 list,
1128 progress,
1129 } => {
1130 let base = format!(
1131 "pmap_chunked {} {{ {} }} {}",
1132 format_expr(chunk_size),
1133 format_block_inline(block),
1134 format_expr(list)
1135 );
1136 match progress {
1137 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1138 None => base,
1139 }
1140 }
1141 ExprKind::PGrepExpr {
1142 block,
1143 list,
1144 progress,
1145 stream: _,
1146 } => {
1147 let base = format!(
1148 "pgrep {{ {} }} {}",
1149 format_block_inline(block),
1150 format_expr(list)
1151 );
1152 match progress {
1153 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1154 None => base,
1155 }
1156 }
1157 ExprKind::ParExpr { block, list } => format!(
1158 "par {{ {} }} {}",
1159 format_block_inline(block),
1160 format_expr(list)
1161 ),
1162 ExprKind::ParReduceExpr {
1163 extract_block,
1164 reduce_block,
1165 list,
1166 } => match reduce_block {
1167 Some(rb) => format!(
1168 "par_reduce {{ {} }} {{ {} }} {}",
1169 format_block_inline(extract_block),
1170 format_block_inline(rb),
1171 format_expr(list)
1172 ),
1173 None => format!(
1174 "par_reduce {{ {} }} {}",
1175 format_block_inline(extract_block),
1176 format_expr(list)
1177 ),
1178 },
1179 ExprKind::PForExpr {
1180 block,
1181 list,
1182 progress,
1183 } => {
1184 let base = format!(
1185 "pfor {{ {} }} {}",
1186 format_block_inline(block),
1187 format_expr(list)
1188 );
1189 match progress {
1190 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1191 None => base,
1192 }
1193 }
1194 ExprKind::ParLinesExpr {
1195 path,
1196 callback,
1197 progress,
1198 } => match progress {
1199 Some(p) => format!(
1200 "par_lines({}, {}, progress => {})",
1201 format_expr(path),
1202 format_expr(callback),
1203 format_expr(p)
1204 ),
1205 None => format!(
1206 "par_lines({}, {})",
1207 format_expr(path),
1208 format_expr(callback)
1209 ),
1210 },
1211 ExprKind::ParWalkExpr {
1212 path,
1213 callback,
1214 progress,
1215 } => match progress {
1216 Some(p) => format!(
1217 "par_walk({}, {}, progress => {})",
1218 format_expr(path),
1219 format_expr(callback),
1220 format_expr(p)
1221 ),
1222 None => format!("par_walk({}, {})", format_expr(path), format_expr(callback)),
1223 },
1224 ExprKind::PwatchExpr { path, callback } => {
1225 format!("pwatch({}, {})", format_expr(path), format_expr(callback))
1226 }
1227 ExprKind::PSortExpr {
1228 cmp,
1229 list,
1230 progress,
1231 } => {
1232 let base = match cmp {
1233 Some(b) => format!(
1234 "psort {{ {} }} {}",
1235 format_block_inline(b),
1236 format_expr(list)
1237 ),
1238 None => format!("psort {}", format_expr(list)),
1239 };
1240 match progress {
1241 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1242 None => base,
1243 }
1244 }
1245 ExprKind::ReduceExpr { block, list } => format!(
1246 "reduce {{ {} }} {}",
1247 format_block_inline(block),
1248 format_expr(list)
1249 ),
1250 ExprKind::PReduceExpr {
1251 block,
1252 list,
1253 progress,
1254 } => {
1255 let base = format!(
1256 "preduce {{ {} }} {}",
1257 format_block_inline(block),
1258 format_expr(list)
1259 );
1260 match progress {
1261 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1262 None => base,
1263 }
1264 }
1265 ExprKind::PReduceInitExpr {
1266 init,
1267 block,
1268 list,
1269 progress,
1270 } => {
1271 let base = format!(
1272 "preduce_init {}, {{ {} }} {}",
1273 format_expr(init),
1274 format_block_inline(block),
1275 format_expr(list)
1276 );
1277 match progress {
1278 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1279 None => base,
1280 }
1281 }
1282 ExprKind::PMapReduceExpr {
1283 map_block,
1284 reduce_block,
1285 list,
1286 progress,
1287 } => {
1288 let base = format!(
1289 "pmap_reduce {{ {} }} {{ {} }} {}",
1290 format_block_inline(map_block),
1291 format_block_inline(reduce_block),
1292 format_expr(list)
1293 );
1294 match progress {
1295 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1296 None => base,
1297 }
1298 }
1299 ExprKind::PcacheExpr {
1300 block,
1301 list,
1302 progress,
1303 } => {
1304 let base = format!(
1305 "pcache {{ {} }} {}",
1306 format_block_inline(block),
1307 format_expr(list)
1308 );
1309 match progress {
1310 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1311 None => base,
1312 }
1313 }
1314 ExprKind::PselectExpr { receivers, timeout } => {
1315 let inner = receivers
1316 .iter()
1317 .map(format_expr)
1318 .collect::<Vec<_>>()
1319 .join(", ");
1320 match timeout {
1321 Some(t) => format!("pselect({}, timeout => {})", inner, format_expr(t)),
1322 None => format!("pselect({})", inner),
1323 }
1324 }
1325 ExprKind::FanExpr {
1326 count,
1327 block,
1328 progress,
1329 capture,
1330 } => {
1331 let kw = if *capture { "fan_cap" } else { "fan" };
1332 let base = match count {
1333 Some(c) => format!(
1334 "{} {} {{ {} }}",
1335 kw,
1336 format_expr(c),
1337 format_block_inline(block)
1338 ),
1339 None => format!("{} {{ {} }}", kw, format_block_inline(block)),
1340 };
1341 match progress {
1342 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1343 None => base,
1344 }
1345 }
1346 ExprKind::AsyncBlock { body } => format!("async {{ {} }}", format_block_inline(body)),
1347 ExprKind::SpawnBlock { body } => format!("spawn {{ {} }}", format_block_inline(body)),
1348 ExprKind::Trace { body } => format!("trace {{ {} }}", format_block_inline(body)),
1349 ExprKind::Timer { body } => format!("timer {{ {} }}", format_block_inline(body)),
1350 ExprKind::Bench { body, times } => format!(
1351 "bench {{ {} }} {}",
1352 format_block_inline(body),
1353 format_expr(times)
1354 ),
1355 ExprKind::Await(e) => format!("await {}", format_expr(e)),
1356 ExprKind::Slurp(e) => format!("slurp {}", format_expr(e)),
1357 ExprKind::Capture(e) => format!("capture {}", format_expr(e)),
1358 ExprKind::Qx(e) => format!("qx {}", format_expr(e)),
1359 ExprKind::FetchUrl(e) => format!("fetch_url {}", format_expr(e)),
1360 ExprKind::Pchannel { capacity } => match capacity {
1361 Some(c) => format!("pchannel({})", format_expr(c)),
1362 None => "pchannel()".to_string(),
1363 },
1364 ExprKind::Push { array, values } => {
1365 format!("push({}, {})", format_expr(array), format_expr_list(values))
1366 }
1367 ExprKind::Pop(e) => format!("pop {}", format_expr(e)),
1368 ExprKind::Shift(e) => format!("shift {}", format_expr(e)),
1369 ExprKind::Unshift { array, values } => format!(
1370 "unshift({}, {})",
1371 format_expr(array),
1372 format_expr_list(values)
1373 ),
1374 ExprKind::Splice {
1375 array,
1376 offset,
1377 length,
1378 replacement,
1379 } => {
1380 let mut parts = vec![format_expr(array)];
1381 if let Some(o) = offset {
1382 parts.push(format_expr(o));
1383 }
1384 if let Some(l) = length {
1385 parts.push(format_expr(l));
1386 }
1387 if !replacement.is_empty() {
1388 parts.push(format_expr_list(replacement));
1389 }
1390 format!("splice({})", parts.join(", "))
1391 }
1392 ExprKind::Delete(e) => format!("delete {}", format_expr(e)),
1393 ExprKind::Exists(e) => format!("exists {}", format_expr(e)),
1394 ExprKind::Keys(e) => format!("keys {}", format_expr(e)),
1395 ExprKind::Values(e) => format!("values {}", format_expr(e)),
1396 ExprKind::Each(e) => format!("each {}", format_expr(e)),
1397 ExprKind::Chomp(e) => format!("chomp {}", format_expr(e)),
1398 ExprKind::Chop(e) => format!("chop {}", format_expr(e)),
1399 ExprKind::Length(e) => format!("length {}", format_expr(e)),
1400 ExprKind::Substr {
1401 string,
1402 offset,
1403 length,
1404 replacement,
1405 } => {
1406 let mut parts = vec![format_expr(string), format_expr(offset)];
1407 if let Some(l) = length {
1408 parts.push(format_expr(l));
1409 }
1410 if let Some(r) = replacement {
1411 parts.push(format_expr(r));
1412 }
1413 format!("substr({})", parts.join(", "))
1414 }
1415 ExprKind::Index {
1416 string,
1417 substr,
1418 position,
1419 } => match position {
1420 Some(p) => format!(
1421 "index({}, {}, {})",
1422 format_expr(string),
1423 format_expr(substr),
1424 format_expr(p)
1425 ),
1426 None => format!("index({}, {})", format_expr(string), format_expr(substr)),
1427 },
1428 ExprKind::Rindex {
1429 string,
1430 substr,
1431 position,
1432 } => match position {
1433 Some(p) => format!(
1434 "rindex({}, {}, {})",
1435 format_expr(string),
1436 format_expr(substr),
1437 format_expr(p)
1438 ),
1439 None => format!("rindex({}, {})", format_expr(string), format_expr(substr)),
1440 },
1441 ExprKind::Sprintf { format, args } => format!(
1442 "sprintf({}, {})",
1443 format_expr(format),
1444 format_expr_list(args)
1445 ),
1446 ExprKind::Abs(e) => format!("abs {}", format_expr(e)),
1447 ExprKind::Int(e) => format!("int {}", format_expr(e)),
1448 ExprKind::Sqrt(e) => format!("sqrt {}", format_expr(e)),
1449 ExprKind::Sin(e) => format!("sin {}", format_expr(e)),
1450 ExprKind::Cos(e) => format!("cos {}", format_expr(e)),
1451 ExprKind::Atan2 { y, x } => format!("atan2({}, {})", format_expr(y), format_expr(x)),
1452 ExprKind::Exp(e) => format!("exp {}", format_expr(e)),
1453 ExprKind::Log(e) => format!("log {}", format_expr(e)),
1454 ExprKind::Rand(opt) => match opt {
1455 Some(e) => format!("rand({})", format_expr(e)),
1456 None => "rand".to_string(),
1457 },
1458 ExprKind::Srand(opt) => match opt {
1459 Some(e) => format!("srand({})", format_expr(e)),
1460 None => "srand".to_string(),
1461 },
1462 ExprKind::Hex(e) => format!("hex {}", format_expr(e)),
1463 ExprKind::Oct(e) => format!("oct {}", format_expr(e)),
1464 ExprKind::Lc(e) => format!("lc {}", format_expr(e)),
1465 ExprKind::Uc(e) => format!("uc {}", format_expr(e)),
1466 ExprKind::Lcfirst(e) => format!("lcfirst {}", format_expr(e)),
1467 ExprKind::Ucfirst(e) => format!("ucfirst {}", format_expr(e)),
1468 ExprKind::Fc(e) => format!("fc {}", format_expr(e)),
1469 ExprKind::Crypt { plaintext, salt } => {
1470 format!("crypt({}, {})", format_expr(plaintext), format_expr(salt))
1471 }
1472 ExprKind::Pos(opt) => match opt {
1473 Some(e) => format!("pos({})", format_expr(e)),
1474 None => "pos".to_string(),
1475 },
1476 ExprKind::Study(e) => format!("study {}", format_expr(e)),
1477 ExprKind::Defined(e) => format!("defined {}", format_expr(e)),
1478 ExprKind::Ref(e) => format!("ref {}", format_expr(e)),
1479 ExprKind::ScalarContext(e) => format!("scalar {}", format_expr(e)),
1480 ExprKind::Chr(e) => format!("chr {}", format_expr(e)),
1481 ExprKind::Ord(e) => format!("ord {}", format_expr(e)),
1482 ExprKind::OpenMyHandle { name } => format!("my ${}", name),
1483 ExprKind::Open { handle, mode, file } => match file {
1484 Some(f) => format!(
1485 "open({}, {}, {})",
1486 format_expr(handle),
1487 format_expr(mode),
1488 format_expr(f)
1489 ),
1490 None => format!("open({}, {})", format_expr(handle), format_expr(mode)),
1491 },
1492 ExprKind::Close(e) => format!("close {}", format_expr(e)),
1493 ExprKind::ReadLine(handle) => match handle {
1494 Some(h) => {
1495 if h.starts_with(|c: char| c.is_uppercase()) {
1496 format!("<{}>", h)
1497 } else {
1498 format!("<${}>", h)
1499 }
1500 }
1501 None => "<STDIN>".to_string(),
1502 },
1503 ExprKind::Eof(opt) => match opt {
1504 Some(e) => format!("eof({})", format_expr(e)),
1505 None => "eof".to_string(),
1506 },
1507 ExprKind::Opendir { handle, path } => {
1508 format!("opendir({}, {})", format_expr(handle), format_expr(path))
1509 }
1510 ExprKind::Readdir(e) => format!("readdir {}", format_expr(e)),
1511 ExprKind::Closedir(e) => format!("closedir {}", format_expr(e)),
1512 ExprKind::Rewinddir(e) => format!("rewinddir {}", format_expr(e)),
1513 ExprKind::Telldir(e) => format!("telldir {}", format_expr(e)),
1514 ExprKind::Seekdir { handle, position } => format!(
1515 "seekdir({}, {})",
1516 format_expr(handle),
1517 format_expr(position)
1518 ),
1519 ExprKind::FileTest { op, expr } => format!("-{}{}", op, format_expr(expr)),
1520 ExprKind::System(args) => format!("system({})", format_expr_list(args)),
1521 ExprKind::Exec(args) => format!("exec({})", format_expr_list(args)),
1522 ExprKind::Eval(e) => format!("eval {}", format_expr(e)),
1523 ExprKind::Do(e) => format!("do {}", format_expr(e)),
1524 ExprKind::Require(e) => format!("require {}", format_expr(e)),
1525 ExprKind::Exit(opt) => match opt {
1526 Some(e) => format!("exit({})", format_expr(e)),
1527 None => "exit".to_string(),
1528 },
1529 ExprKind::Chdir(e) => format!("chdir {}", format_expr(e)),
1530 ExprKind::Mkdir { path, mode } => match mode {
1531 Some(m) => format!("mkdir({}, {})", format_expr(path), format_expr(m)),
1532 None => format!("mkdir({})", format_expr(path)),
1533 },
1534 ExprKind::Unlink(args) => format!("unlink({})", format_expr_list(args)),
1535 ExprKind::Rename { old, new } => {
1536 format!("rename({}, {})", format_expr(old), format_expr(new))
1537 }
1538 ExprKind::Chmod(args) => format!("chmod({})", format_expr_list(args)),
1539 ExprKind::Chown(args) => format!("chown({})", format_expr_list(args)),
1540 ExprKind::Stat(e) => format!("stat {}", format_expr(e)),
1541 ExprKind::Lstat(e) => format!("lstat {}", format_expr(e)),
1542 ExprKind::Link { old, new } => format!("link({}, {})", format_expr(old), format_expr(new)),
1543 ExprKind::Symlink { old, new } => {
1544 format!("symlink({}, {})", format_expr(old), format_expr(new))
1545 }
1546 ExprKind::Readlink(e) => format!("readlink {}", format_expr(e)),
1547 ExprKind::Glob(args) => format!("glob({})", format_expr_list(args)),
1548 ExprKind::Files(args) => format!("files({})", format_expr_list(args)),
1549 ExprKind::Filesf(args) => format!("filesf({})", format_expr_list(args)),
1550 ExprKind::FilesfRecursive(args) => format!("fr({})", format_expr_list(args)),
1551 ExprKind::Dirs(args) => format!("dirs({})", format_expr_list(args)),
1552 ExprKind::DirsRecursive(args) => format!("dr({})", format_expr_list(args)),
1553 ExprKind::SymLinks(args) => format!("sym_links({})", format_expr_list(args)),
1554 ExprKind::Sockets(args) => format!("sockets({})", format_expr_list(args)),
1555 ExprKind::Pipes(args) => format!("pipes({})", format_expr_list(args)),
1556 ExprKind::BlockDevices(args) => format!("block_devices({})", format_expr_list(args)),
1557 ExprKind::CharDevices(args) => format!("char_devices({})", format_expr_list(args)),
1558 ExprKind::Executables(args) => format!("exe({})", format_expr_list(args)),
1559 ExprKind::GlobPar { args, progress } => {
1560 let base = format!("glob_par({})", format_expr_list(args));
1561 match progress {
1562 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1563 None => base,
1564 }
1565 }
1566 ExprKind::ParSed { args, progress } => {
1567 let base = format!("par_sed({})", format_expr_list(args));
1568 match progress {
1569 Some(p) => format!("{}, progress => {}", base, format_expr(p)),
1570 None => base,
1571 }
1572 }
1573 ExprKind::Bless { ref_expr, class } => match class {
1574 Some(c) => format!("bless({}, {})", format_expr(ref_expr), format_expr(c)),
1575 None => format!("bless({})", format_expr(ref_expr)),
1576 },
1577 ExprKind::Caller(opt) => match opt {
1578 Some(e) => format!("caller({})", format_expr(e)),
1579 None => "caller".to_string(),
1580 },
1581 ExprKind::Wantarray => "wantarray".to_string(),
1582 ExprKind::List(exprs) => format!("({})", format_expr_list(exprs)),
1583 ExprKind::PostfixIf { expr, condition } => {
1584 format!("{} if {}", format_expr(expr), format_expr(condition))
1585 }
1586 ExprKind::PostfixUnless { expr, condition } => {
1587 format!("{} unless {}", format_expr(expr), format_expr(condition))
1588 }
1589 ExprKind::PostfixWhile { expr, condition } => {
1590 format!("{} while {}", format_expr(expr), format_expr(condition))
1591 }
1592 ExprKind::PostfixUntil { expr, condition } => {
1593 format!("{} until {}", format_expr(expr), format_expr(condition))
1594 }
1595 ExprKind::PostfixForeach { expr, list } => {
1596 format!("{} foreach {}", format_expr(expr), format_expr(list))
1597 }
1598 ExprKind::AlgebraicMatch { subject, arms } => {
1599 let arms_s = arms
1600 .iter()
1601 .map(|a| {
1602 let guard_s = a
1603 .guard
1604 .as_ref()
1605 .map(|g| format!(" if {}", format_expr(g)))
1606 .unwrap_or_default();
1607 format!(
1608 "{}{} => {}",
1609 format_match_pattern(&a.pattern),
1610 guard_s,
1611 format_expr(&a.body)
1612 )
1613 })
1614 .collect::<Vec<_>>()
1615 .join(", ");
1616 format!("match ({}) {{ {} }}", format_expr(subject), arms_s)
1617 }
1618 ExprKind::RetryBlock {
1619 body,
1620 times,
1621 backoff,
1622 } => {
1623 let bo = match backoff {
1624 crate::ast::RetryBackoff::None => "none",
1625 crate::ast::RetryBackoff::Linear => "linear",
1626 crate::ast::RetryBackoff::Exponential => "exponential",
1627 };
1628 format!(
1629 "retry {{ {} }} times => {}, backoff => {}",
1630 format_block_inline(body),
1631 format_expr(times),
1632 bo
1633 )
1634 }
1635 ExprKind::RateLimitBlock {
1636 max, window, body, ..
1637 } => {
1638 format!(
1639 "rate_limit({}, {}) {{ {} }}",
1640 format_expr(max),
1641 format_expr(window),
1642 format_block_inline(body)
1643 )
1644 }
1645 ExprKind::EveryBlock { interval, body } => {
1646 format!(
1647 "every({}) {{ {} }}",
1648 format_expr(interval),
1649 format_block_inline(body)
1650 )
1651 }
1652 ExprKind::GenBlock { body } => {
1653 format!("gen {{ {} }}", format_block_inline(body))
1654 }
1655 ExprKind::Yield(e) => {
1656 format!("yield {}", format_expr(e))
1657 }
1658 ExprKind::Spinner { message, body } => {
1659 format!(
1660 "spinner {} {{ {} }}",
1661 format_expr(message),
1662 body.iter()
1663 .map(format_statement)
1664 .collect::<Vec<_>>()
1665 .join("; ")
1666 )
1667 }
1668 ExprKind::MyExpr { keyword, decls } => {
1669 let parts: Vec<String> = decls
1672 .iter()
1673 .map(|d| {
1674 let sigil = match d.sigil {
1675 crate::ast::Sigil::Scalar => '$',
1676 crate::ast::Sigil::Array => '@',
1677 crate::ast::Sigil::Hash => '%',
1678 crate::ast::Sigil::Typeglob => '*',
1679 };
1680 let mut s = format!("{}{}", sigil, d.name);
1681 if let Some(init) = &d.initializer {
1682 s.push_str(" = ");
1683 s.push_str(&format_expr(init));
1684 }
1685 s
1686 })
1687 .collect();
1688 if parts.len() == 1 {
1689 format!("{} {}", keyword, parts[0])
1690 } else {
1691 format!("{} ({})", keyword, parts.join(", "))
1692 }
1693 }
1694 }
1695}
1696
1697pub(crate) fn format_match_pattern(p: &crate::ast::MatchPattern) -> String {
1698 use crate::ast::{MatchArrayElem, MatchHashPair, MatchPattern};
1699 match p {
1700 MatchPattern::Any => "_".to_string(),
1701 MatchPattern::Regex { pattern, flags } => {
1702 if flags.is_empty() {
1703 format!("/{}/", pattern)
1704 } else {
1705 format!("/{}/{}/", pattern, flags)
1706 }
1707 }
1708 MatchPattern::Value(e) => format_expr(e),
1709 MatchPattern::Array(elems) => {
1710 let inner = elems
1711 .iter()
1712 .map(|x| match x {
1713 MatchArrayElem::Expr(e) => format_expr(e),
1714 MatchArrayElem::CaptureScalar(name) => format!("${}", name),
1715 MatchArrayElem::Rest => "*".to_string(),
1716 MatchArrayElem::RestBind(name) => format!("@{}", name),
1717 })
1718 .collect::<Vec<_>>()
1719 .join(", ");
1720 format!("[{}]", inner)
1721 }
1722 MatchPattern::Hash(pairs) => {
1723 let inner = pairs
1724 .iter()
1725 .map(|pair| match pair {
1726 MatchHashPair::KeyOnly { key } => {
1727 format!("{} => _", format_expr(key))
1728 }
1729 MatchHashPair::Capture { key, name } => {
1730 format!("{} => ${}", format_expr(key), name)
1731 }
1732 })
1733 .collect::<Vec<_>>()
1734 .join(", ");
1735 format!("{{ {} }}", inner)
1736 }
1737 MatchPattern::OptionSome(name) => format!("Some({})", name),
1738 }
1739}
1740
1741#[cfg(test)]
1742mod tests {
1743 use super::*;
1744 use crate::parse;
1745
1746 #[test]
1747 fn format_program_expression_statement_includes_binop() {
1748 let p = parse("2 + 3").expect("parse");
1749 let out = format_program(&p);
1750 assert!(
1751 out.contains("2") && out.contains("3") && out.contains("+"),
1752 "unexpected format: {out}"
1753 );
1754 }
1755
1756 #[test]
1757 fn format_program_if_block() {
1758 let p = parse("if (1) { 2; }").expect("parse");
1759 let out = format_program(&p);
1760 assert!(out.contains("if") && out.contains('1'));
1761 }
1762
1763 #[test]
1764 fn format_program_package_line() {
1765 let p = parse("package Foo::Bar").expect("parse");
1766 let out = format_program(&p);
1767 assert!(out.contains("package Foo::Bar"));
1768 }
1769
1770 #[test]
1771 fn format_program_string_literal_escapes_quote() {
1772 let p = parse(r#"my $s = "a\"b""#).expect("parse");
1773 let out = format_program(&p);
1774 assert!(out.contains("\\\""), "expected escaped quote in: {out}");
1775 }
1776}