1#![expect(clippy::ptr_arg, reason = "Follow original type to use `&mut Vec<_>`")]
2use crate::{Pos, Redirect, RedirectPort, SourceFile, Stmt, SwitchCase, Word, WordFrag};
3
4macro_rules! define_visitor {
5 (
6 $i:lifetime;
7 $(
8 fn $func:ident ($v:ident $(,$arg:ident : $arg_ty:ty)* $(,)?)
9 $func_body:block
10 )*
11 ) => {
12 pub trait VisitorMut<$i> {
13 $(
14 fn $func(&mut self, $($arg : $arg_ty),*) {
15 $func(self, $($arg),*);
16 }
17 )*
18 }
19
20 $(
21 pub fn $func<$i, V: VisitorMut<$i> + ?Sized>($v: &mut V, $($arg : $arg_ty),*)
22 $func_body
23 )*
24 };
25}
26
27define_visitor! {
28 'i;
29
30 fn visit_source_file_mut(v, file: &'i mut SourceFile) {
31 file.stmts.iter_mut().for_each(|s| v.visit_stmt_mut(s));
32 }
33
34 fn visit_stmt_mut(v, s: &'i mut Stmt) {
35 match s {
36 Stmt::Command(pos, ws) => v.visit_command_stmt_mut(*pos, ws),
37 Stmt::Block(pos, b) => v.visit_block_stmt_mut(*pos, b),
38 Stmt::If(pos, cond, then, else_) => v.visit_if_stmt_mut(*pos, cond, then, else_.as_deref_mut()),
39 Stmt::For(pos, var, seq, body) => v.visit_for_stmt_mut(*pos, var, seq, body),
40 Stmt::While(pos, cond, body) => v.visit_while_stmt_mut(*pos, cond, body),
41 Stmt::Break(pos) => v.visit_break_stmt_mut(*pos),
42 Stmt::Continue(pos) => v.visit_continue_stmt_mut(*pos),
43 Stmt::Function(pos, def, body) => v.visit_function_stmt_mut(*pos, def, body),
44 Stmt::Return(pos, w) => v.visit_return_stmt_mut(*pos, w.as_mut()),
45 Stmt::Switch(pos, testee, cases) => v.visit_switch_stmt_mut(*pos, testee, cases),
46 Stmt::Redirect(pos, s, redirects) => v.visit_redirect_stmt_mut(*pos, s, redirects),
47 Stmt::Pipe(pos, pipes, rhs) => v.visit_pipe_stmt_mut(*pos, pipes, rhs),
48 Stmt::Not(pos, s) => v.visit_not_stmt_mut(*pos, s),
49 Stmt::UnaryAnd(pos, s) => v.visit_unary_and_stmt_mut(*pos, s),
50 Stmt::UnaryOr(pos, s) => v.visit_unary_or_stmt_mut(*pos, s),
51 Stmt::BinaryAnd(pos, lhs, rhs) => v.visit_binary_and_stmt_mut(*pos, lhs, rhs),
52 Stmt::BinaryOr(pos, lhs, rhs) => v.visit_binary_or_stmt_mut(*pos, lhs, rhs),
53 }
54 }
55
56 fn visit_command_stmt_mut(v, _pos: Pos, ws: &'i mut Vec<Word>) {
57 ws.iter_mut().for_each(|w| v.visit_word_mut(w));
58 }
59
60 fn visit_block_stmt_mut(v, _pos: Pos, stmts: &'i mut Vec<Stmt>) {
61 stmts.iter_mut().for_each(|w| v.visit_stmt_mut(w));
62 }
63
64 fn visit_if_stmt_mut(v, _pos: Pos, cond: &'i mut Stmt, then: &'i mut Stmt, else_: Option<&'i mut Stmt>) {
65 v.visit_stmt_mut(cond);
66 v.visit_stmt_mut(then);
67 if let Some(else_) = else_ {
68 v.visit_stmt_mut(else_);
69 }
70 }
71
72 fn visit_for_stmt_mut(v, _pos: Pos, var: &'i mut Word, seq: &'i mut Vec<Word>, body: &'i mut Stmt) {
73 v.visit_word_mut(var);
74 seq.iter_mut().for_each(|w| v.visit_word_mut(w));
75 v.visit_stmt_mut(body);
76 }
77
78 fn visit_while_stmt_mut(v, _pos: Pos, cond: &'i mut Stmt, body: &'i mut Stmt) {
79 v.visit_stmt_mut(cond);
80 v.visit_stmt_mut(body);
81 }
82
83 fn visit_break_stmt_mut(_v, _pos: Pos) {}
84
85 fn visit_continue_stmt_mut(_v, _pos: Pos) {}
86
87 fn visit_function_stmt_mut(v, _pos: Pos, def: &'i mut Vec<Word>, body: &'i mut Stmt) {
88 def.iter_mut().for_each(|w| v.visit_word_mut(w));
89 v.visit_stmt_mut(body);
90 }
91
92 fn visit_return_stmt_mut(v, _pos: Pos, arg: Option<&'i mut Word>) {
93 if let Some(arg) = arg {
94 v.visit_word_mut(arg);
95 }
96 }
97
98 fn visit_switch_stmt_mut(v, _pos: Pos, testee: &'i mut Word, cases: &'i mut Vec<SwitchCase>) {
99 v.visit_word_mut(testee);
100 cases.iter_mut().for_each(|case| v.visit_switch_case_mut(case));
101 }
102
103 fn visit_redirect_stmt_mut(v, _pos: Pos, s: &'i mut Stmt, redirects: &'i mut Vec<Redirect>) {
104 v.visit_stmt_mut(s);
105 redirects.iter_mut().for_each(|redir| v.visit_redirect_mut(redir));
106 }
107
108 fn visit_pipe_stmt_mut(v, _pos: Pos, pipes: &'i mut Vec<(Stmt, RedirectPort)>, rhs: &'i mut Stmt) {
109 for (lhs, port) in pipes {
110 v.visit_stmt_mut(lhs);
111 v.visit_redirect_port_mut(port);
112 }
113 v.visit_stmt_mut(rhs);
114 }
115
116 fn visit_not_stmt_mut(v, _pos: Pos, s: &'i mut Stmt) {
117 v.visit_stmt_mut(s);
118 }
119
120 fn visit_unary_and_stmt_mut(v, _pos: Pos, s: &'i mut Stmt) {
121 v.visit_stmt_mut(s);
122 }
123
124 fn visit_unary_or_stmt_mut(v, _pos: Pos, s: &'i mut Stmt) {
125 v.visit_stmt_mut(s);
126 }
127
128 fn visit_binary_and_stmt_mut(v, _pos: Pos, lhs: &'i mut Stmt, rhs: &'i mut Stmt) {
129 v.visit_stmt_mut(lhs);
130 v.visit_stmt_mut(rhs);
131 }
132
133 fn visit_binary_or_stmt_mut(v, _pos: Pos, lhs: &'i mut Stmt, rhs: &'i mut Stmt) {
134 v.visit_stmt_mut(lhs);
135 v.visit_stmt_mut(rhs);
136 }
137
138 fn visit_switch_case_mut(v, case: &'i mut SwitchCase) {
139 case.globs.iter_mut().for_each(|w| v.visit_word_mut(w));
140 v.visit_stmt_mut(&mut case.body);
141 }
142
143 fn visit_redirect_mut(v, redirect: &'i mut Redirect) {
144 v.visit_redirect_port_mut(&mut redirect.port);
145 }
146
147 fn visit_redirect_port_mut(_v, _port: &'i mut RedirectPort) {}
148
149 fn visit_word_mut(v, w: &'i mut Word) {
150 match w {
151 Word::Simple(_) => {}
152 Word::Complex(frags) => frags.iter_mut().for_each(|f| v.visit_word_frag_mut(f)),
153 }
154 }
155
156 fn visit_word_frag_mut(v, frag: &'i mut WordFrag) {
157 match frag {
158 WordFrag::Literal(_) |
159 WordFrag::Variable { .. } |
160 WordFrag::VariableNoSplit { .. } |
161 WordFrag::Home { .. } |
162 WordFrag::Wildcard |
163 WordFrag::WildcardRecursive => {}
164 WordFrag::Command(s) |
165 WordFrag::CommandNoSplit(s) => v.visit_stmt_mut(s),
166 WordFrag::Brace(ws) => ws.iter_mut().for_each(|w| v.visit_word_mut(w)),
167 }
168 }
169}