1use crate::ast::{ASTNode, CmdEvalArgs, GroupType, LiftAST, VAN};
2use delegate_attr::delegate;
3use itertools::Itertools;
4
5pub trait MangleAST: Default {
8 fn to_str(self, escc: char) -> String;
9
10 fn get_complexity(&self) -> usize;
12
13 #[inline(always)]
14 fn take(&mut self) -> Self {
15 std::mem::take(self)
16 }
17
18 #[inline]
19 fn simplify_inplace(&mut self) {
20 *self = self.take().simplify();
21 }
22
23 fn simplify(self) -> Self;
25
26 fn apply_arguments_inplace(&mut self, args: &CmdEvalArgs) -> Result<(), usize>;
30}
31
32impl MangleAST for ASTNode {
33 fn to_str(self, escc: char) -> String {
34 use ASTNode::*;
35 match self {
36 NullNode => String::new(),
37 Constant(_, x) => x.to_string(),
38 Grouped(gt, elems) => {
39 let inner = elems.to_str(escc);
40 if gt == GroupType::Strict {
41 format!("({})", inner)
42 } else {
43 inner
44 }
45 }
46 Argument { indirection, index } => std::iter::repeat('$')
47 .take(indirection + 1)
48 .chain(
49 index
50 .as_ref()
51 .map(usize::to_string)
52 .iter()
53 .flat_map(|i| i.chars()),
54 )
55 .collect(),
56 CmdEval(cmd, args) => format!("{}({}{})", escc, cmd.to_str(escc), args.to_str(escc)),
57 }
58 }
59
60 fn get_complexity(&self) -> usize {
61 use ASTNode::*;
62 match &self {
63 NullNode => 0,
64 Constant(_, x) => 1 + x.len(),
65 Argument { indirection, .. } => 3 + indirection,
66 Grouped(gt, x) => {
67 (match *gt {
68 GroupType::Dissolving => 0,
69 GroupType::Loose => 1,
70 GroupType::Strict => 2,
71 }) + x.get_complexity()
72 }
73 CmdEval(cmd, x) => 1 + cmd.get_complexity() + x.get_complexity(),
74 }
75 }
76
77 fn simplify(mut self) -> Self {
78 use ASTNode::*;
79 let mut cplx = self.get_complexity();
80 loop {
81 match &mut self {
82 Grouped(ref mut gt, ref mut x) => {
83 match x.len() {
84 0 => {
85 if *gt != GroupType::Strict {
86 self = NullNode;
87 }
88 }
89 1 => {
90 let y = x[0].take().simplify();
91 if *gt != GroupType::Strict {
92 self = y;
93 } else if let Grouped(GroupType::Dissolving, z) = y {
94 *x = z;
95 } else {
96 x[0] = y;
98 }
99 }
100 _ => x.simplify_inplace(),
101 }
102 }
103 CmdEval(ref mut cmd, ref mut args) => {
104 cmd.simplify_inplace();
105 args.simplify_inplace();
106 }
107 _ => break,
108 }
109 let new_cplx = self.get_complexity();
110 if new_cplx >= cplx {
111 break;
112 }
113 cplx = new_cplx;
114 }
115 self
116 }
117
118 fn apply_arguments_inplace(&mut self, xargs: &CmdEvalArgs) -> Result<(), usize> {
119 use ASTNode::*;
120 match self {
121 Argument {
122 indirection: 0,
123 index,
124 } => {
125 *self = match *index {
126 Some(index) => match xargs.0.get(index) {
127 Some(x) => x.clone(),
128 None => return Err(index),
129 },
130 None => Constant(true, crulst_atom!("$")),
131 };
132 }
133 Argument {
134 ref mut indirection,
135 ..
136 } => *indirection -= 1,
137
138 Grouped(_, ref mut x) => x.apply_arguments_inplace(xargs)?,
139 CmdEval(ref mut cmd, ref mut args) => {
140 cmd.apply_arguments_inplace(xargs)?;
141 args.apply_arguments_inplace(xargs)?;
142 }
143 _ => {}
144 }
145 Ok(())
146 }
147}
148
149impl MangleAST for VAN {
150 fn to_str(self, escc: char) -> String {
151 self.into_iter()
152 .fold(String::new(), |acc, i| acc + &i.to_str(escc))
153 }
154
155 #[inline]
156 fn get_complexity(&self) -> usize {
157 self.iter().map(|i| i.get_complexity()).sum()
158 }
159
160 fn simplify(self) -> Self {
161 #[derive(PartialEq)]
162 enum ASTNodeClass {
163 NullNode,
164 Constant(bool),
165 Grouped(GroupType),
166 Opaque,
167 }
168
169 self.into_iter()
170 .map(|i| i.simplify())
171 .group_by(|i| {
172 use ASTNodeClass::*;
173 match i {
174 ASTNode::Grouped(gt, x) if x.is_empty() && *gt != GroupType::Strict => NullNode,
175 ASTNode::Constant(_, x) if x.is_empty() => NullNode,
176 ASTNode::Constant(s, _) => Constant(*s),
177 ASTNode::Grouped(s, _) => Grouped(*s),
178 ASTNode::Argument { .. } | ASTNode::CmdEval(_, _) => Opaque,
179 _ => NullNode,
180 }
181 })
182 .into_iter()
183 .filter(|(d, _)| *d != ASTNodeClass::NullNode)
184 .flat_map(|(d, i)| {
185 use ASTNode::*;
186 match d {
187 ASTNodeClass::Constant(x) => Constant(
188 x,
189 i.map(|j| {
190 if let Constant(_, y) = j {
191 y
192 } else {
193 unreachable!()
194 }
195 })
196 .fold(String::new(), |acc, i| acc + &i)
197 .into(),
198 )
199 .lift_ast(),
200 ASTNodeClass::Grouped(GroupType::Dissolving) => i
201 .flat_map(|j| {
202 if let Grouped(_, x) = j {
203 x
204 } else {
205 unreachable!()
206 }
207 })
208 .collect(),
209 _ => i.collect(),
210 }
211 })
212 .collect()
213 }
214
215 fn apply_arguments_inplace(&mut self, args: &CmdEvalArgs) -> Result<(), usize> {
216 for i in self.iter_mut() {
217 i.apply_arguments_inplace(args)?;
218 }
219 Ok(())
220 }
221}
222
223impl MangleAST for CmdEvalArgs {
224 fn to_str(self, escc: char) -> String {
225 self.0
226 .into_iter()
227 .fold(String::new(), |acc, i| acc + " " + &i.to_str(escc))
228 }
229
230 fn simplify(self) -> Self {
231 self.into_iter()
232 .map(|i| i.simplify())
233 .flat_map(|i| {
234 if let ASTNode::Grouped(GroupType::Dissolving, elems) = i {
235 elems
236 } else {
237 i.lift_ast()
238 }
239 })
240 .collect()
241 }
242
243 #[delegate(self.0)]
244 fn get_complexity(&self) -> usize { }
245
246 #[delegate(self.0)]
247 fn apply_arguments_inplace(&mut self, args: &CmdEvalArgs) -> Result<(), usize> { }
248}
249
250pub trait MangleASTExt: MangleAST {
251 fn compact_toplevel(self) -> Self;
252}
253
254impl MangleASTExt for VAN {
255 fn compact_toplevel(self) -> Self {
256 self.into_iter()
259 .flat_map(|i| match i {
261 ASTNode::NullNode => vec![],
262 ASTNode::Grouped(gt, x) if gt != GroupType::Strict => x.compact_toplevel(),
263 _ => vec![i],
264 })
265 .peekable()
267 .batching(|it| {
268 let (mut risp, mut rdat) = match it.next()? {
269 ASTNode::Constant(isp, dat) => (isp, dat.to_string()),
270 x => return Some(x),
271 };
272 while let Some(ASTNode::Constant(isp, ref dat)) = it.peek() {
273 risp |= isp;
274 rdat += &dat;
275 it.next();
276 }
277 Some(ASTNode::Constant(risp, rdat.into()))
278 })
279 .collect()
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::ASTNode::*;
286 use super::*;
287
288 #[test]
289 fn test_simplify() {
290 let ast = vec![
291 Constant(true, "a".into()),
292 Constant(true, "b".into())
293 .lift_ast()
294 .lift_ast()
295 .lift_ast()
296 .lift_ast(),
297 Constant(true, "c".into()),
298 ]
299 .lift_ast()
300 .lift_ast()
301 .lift_ast();
302 assert_eq!(ast.simplify(), Constant(true, "abc".into()));
303 }
304
305 #[test]
306 fn test_compact_tl() {
307 let ast = vec![
308 Constant(true, "a".into()),
309 Constant(false, "b".into()),
310 Constant(true, "c".into()),
311 ]
312 .compact_toplevel();
313 assert_eq!(ast, vec![Constant(true, "abc".into())]);
314 }
315}