Skip to main content

ghoti_syntax/
visit.rs

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}