1use bool_logic::ast::{All, Any, Not, Var, any, expr};
2use bool_logic::cfg::ast::{Expr, Pred, flag, target_family};
3use bool_logic::visit_mut::{VisitMut, walk_mut_expr, walk_mut_expr_list};
4
5use bool_logic::transforms::dedup_list::DedupList;
6use bool_logic::transforms::eval_const::EvalConst;
7use bool_logic::transforms::flatten_nested_list::FlattenNestedList;
8use bool_logic::transforms::flatten_single::FlattenSingle;
9use bool_logic::transforms::merge_all_of_any::MergeAllOfAny;
10use bool_logic::transforms::merge_all_of_not_any::MergeAllOfNotAny;
11use bool_logic::transforms::simplify_all_not_any::SimplifyAllNotAny;
12use bool_logic::transforms::simplify_by_short_circuit::SimplifyByShortCircuit;
13use bool_logic::transforms::simplify_nested_list::SimplifyNestedList;
14
15use std::cmp::Ordering;
16use std::cmp::Ordering::{Equal, Greater, Less};
17use std::mem;
18
19use stdx::iter::filter_map_collect_vec;
20use stdx::iter::map_collect_vec;
21use stdx::vec::VecExt;
22
23use log::debug;
24use log::trace;
25
26pub fn simplified_expr(x: impl Into<Expr>) -> Expr {
27 let mut x = x.into();
28
29 debug!("input: {x}");
30
31 UnifyTargetFamily.visit_mut_expr(&mut x);
32 trace!("after UnifyTargetFamily: {x}");
33
34 for _ in 0..3 {
35 FlattenSingle.visit_mut_expr(&mut x);
36 trace!("after FlattenSingle: {x}");
37
38 FlattenNestedList.visit_mut_expr(&mut x);
39 trace!("after FlattenNestedList: {x}");
40
41 DedupList.visit_mut_expr(&mut x);
42 trace!("after DedupList: {x}");
43
44 EvalConst.visit_mut_expr(&mut x);
45 trace!("after EvalConst: {x}");
46
47 SimplifyNestedList.visit_mut_expr(&mut x);
48 trace!("after SimplifyNestedList: {x}");
49
50 MergeAllOfNotAny.visit_mut_expr(&mut x);
51 trace!("after MergeAllOfNotAny: {x}");
52
53 SimplifyAllNotAny.visit_mut_expr(&mut x);
54 trace!("after SimplifyAllNotAny: {x}");
55
56 MergeAllOfAny.visit_mut_expr(&mut x);
57 trace!("after MergeAllOfAny: {x}");
58
59 ImplyByKey.visit_mut_expr(&mut x);
60 trace!("after ImplyByKey: {x}");
61
62 SuppressTargetFamily.visit_mut_expr(&mut x);
63 trace!("after SuppressTargetFamily: {x}");
64
65 EvalConst.visit_mut_expr(&mut x);
66 trace!("after EvalConst: {x}");
67
68 MergePattern.visit_mut_expr(&mut x);
69 trace!("after MergePattern: {x}");
70
71 EvalConst.visit_mut_expr(&mut x);
72 trace!("after EvalConst: {x}");
73
74 SimplifyByShortCircuit.visit_mut_expr(&mut x);
75 trace!("after SimplifyByShortCircuit: {x}");
76
77 EvalConst.visit_mut_expr(&mut x);
78 trace!("after EvalConst: {x}");
79 }
80
81 SimplifyTargetFamily.visit_mut_expr(&mut x);
82 trace!("after SimplifyTargetFamily: {x}");
83
84 SortByPriority.visit_mut_expr(&mut x);
85 trace!("after SortByPriority: {x}");
86
87 SortByValue.visit_mut_expr(&mut x);
88 trace!("after SortByValue: {x}");
89
90 debug!("output: {x}");
91
92 x
93}
94
95struct SortByPriority;
96
97impl SortByPriority {
98 fn get_priority(x: &Expr) -> u32 {
99 match x {
100 Expr::Not(_) => 103,
101 Expr::Any(_) => 101,
102 Expr::All(_) => 102,
103 Expr::Var(Var(pred)) => match pred.key.as_str() {
104 "target_family" => 1,
105 "target_arch" => 2,
106 "target_vendor" => 3,
107 "target_os" => 4,
108 "target_env" => 5,
109 "target_pointer_width" => 6,
110 _ => 0,
111 },
112 Expr::Const(_) => panic!(),
113 }
114 }
115}
116
117impl VisitMut<Pred> for SortByPriority {
118 fn visit_mut_expr(&mut self, expr: &mut Expr) {
119 if let Some(list) = expr.as_mut_expr_list() {
120 list.sort_by(|lhs, rhs| {
121 let lhs = Self::get_priority(lhs);
122 let rhs = Self::get_priority(rhs);
123 lhs.cmp(&rhs)
124 });
125 }
126
127 walk_mut_expr(self, expr);
128 }
129}
130
131struct SortByValue;
132
133impl SortByValue {
134 fn cmp_var(lhs: &Expr, rhs: &Expr) -> Ordering {
135 let Expr::Var(Var(lhs)) = lhs else {
136 return Equal;
137 };
138 let Expr::Var(Var(rhs)) = rhs else {
139 return Equal;
140 };
141
142 let ok = Ord::cmp(lhs.key.as_str(), rhs.key.as_str());
143
144 match (lhs.value.as_deref(), rhs.value.as_deref()) {
145 (None, None) => ok,
146 (Some(lv), Some(rv)) => ok.then_with(|| Ord::cmp(lv, rv)),
147 (None, Some(_)) => Less,
148 (Some(_), None) => Greater,
149 }
150 }
151
152 fn cmp_not(lhs: &Expr, rhs: &Expr) -> Ordering {
153 let Expr::Not(Not(lhs)) = lhs else {
154 return Equal;
155 };
156 let Expr::Not(Not(rhs)) = rhs else {
157 return Equal;
158 };
159
160 Self::cmp_var(lhs, rhs)
161 }
162}
163
164impl VisitMut<Pred> for SortByValue {
165 fn visit_mut_expr(&mut self, expr: &mut Expr) {
166 if let Some(list) = expr.as_mut_expr_list() {
167 list.sort_by(Self::cmp_var);
168 list.sort_by(Self::cmp_not);
169 }
170
171 walk_mut_expr(self, expr);
172 }
173}
174
175struct UnifyTargetFamily;
176
177impl VisitMut<Pred> for UnifyTargetFamily {
178 fn visit_mut_var(&mut self, Var(pred): &mut Var<Pred>) {
179 if pred.value.is_none() && matches!(pred.key.as_str(), "unix" | "windows" | "wasm") {
180 *pred = target_family(pred.key.clone());
181 }
182 }
183}
184
185struct SimplifyTargetFamily;
186
187impl VisitMut<Pred> for SimplifyTargetFamily {
188 fn visit_mut_var(&mut self, Var(pred): &mut Var<Pred>) {
189 if pred.key == "target_family" {
190 if let Some(value) = pred.value.as_deref() {
191 if matches!(value, "unix" | "windows" | "wasm") {
192 *pred = flag(value);
193 }
194 }
195 }
196 }
197}
198
199struct ImplyByKey;
200
201impl ImplyByKey {
202 const UNIQUE_VALUED_KEYS: &'static [&'static str] = &[
203 "target_family",
204 "target_arch",
205 "target_vendor",
206 "target_os",
207 "target_env",
208 "target_pointer_width",
209 ];
210
211 fn is_expr_any_pred(any: &[Expr], key: &str) -> bool {
212 any.iter()
213 .all(|x| x.as_var().is_some_and(|Var(var)| var.key == key))
214 }
215
216 fn fix(pos_key: &str, pos_any_values: &[&str], expr: &mut Expr) {
217 match expr {
218 Expr::Any(Any(any)) => {
219 for x in any.iter_mut() {
220 Self::fix(pos_key, pos_any_values, x);
221 }
222 }
223 Expr::All(All(all)) => {
224 for x in all.iter_mut() {
225 Self::fix(pos_key, pos_any_values, x);
226 }
227 }
228 Expr::Not(Not(not)) => {
229 Self::fix(pos_key, pos_any_values, not);
230 }
231 Expr::Var(Var(var)) => {
232 if var.key == pos_key {
233 let var_value = var.value.as_deref().unwrap();
234 if pos_any_values.contains(&var_value) {
235 if pos_any_values.len() == 1 {
236 *expr = Expr::Const(true);
237 }
238 } else {
239 *expr = Expr::Const(false);
240 }
241 }
242 }
243 Expr::Const(_) => {}
244 }
245 }
246}
247
248impl VisitMut<Pred> for ImplyByKey {
249 fn visit_mut_all(&mut self, All(all): &mut All<Pred>) {
250 walk_mut_expr_list(self, all);
251
252 let mut i = 0;
253 while i < all.len() {
254 match &all[i] {
255 Expr::Var(Var(pos)) if Self::UNIQUE_VALUED_KEYS.contains(&pos.key.as_str()) => {
256 assert!(pos.value.is_some());
257
258 let pos = pos.clone();
259 let pos_key = pos.key.as_str();
260 let pos_any_values = &[pos.value.as_deref().unwrap()];
261
262 for (_, x) in all.iter_mut().enumerate().filter(|&(j, _)| j != i) {
263 Self::fix(pos_key, pos_any_values, x);
264 }
265 }
266 Expr::Any(Any(any)) => {
267 if let Some(pos_key) = Self::UNIQUE_VALUED_KEYS
268 .iter()
269 .find(|k| Self::is_expr_any_pred(any, k))
270 {
271 let any = any.clone();
272 let pos_any_values = map_collect_vec(&any, |x| {
273 x.as_var().unwrap().0.value.as_deref().unwrap()
274 });
275
276 for (_, x) in all.iter_mut().enumerate().filter(|&(j, _)| j != i) {
277 Self::fix(pos_key, &pos_any_values, x);
278 }
279 }
280 }
281 _ => {}
282 }
283 i += 1;
284 }
285 }
286}
287
288struct SuppressTargetFamily;
289
290impl SuppressTargetFamily {
291 fn is_family_implier(x: &Expr) -> bool {
292 match x {
293 Expr::Var(Var(var)) => match (var.key.as_str(), var.value.as_deref()) {
294 ("target_os", _) | ("target_vendor", Some("apple")) => true,
297 _ => false,
298 },
299 _ => false,
300 }
301 }
302
303 fn has_specified_family_implier(x: &Expr) -> bool {
304 if Self::is_family_implier(x) {
305 return true;
306 }
307
308 if let Expr::Any(Any(any)) = x {
309 return any.iter().all(Self::is_family_implier);
310 }
311
312 false
313 }
314
315 #[allow(clippy::match_like_matches_macro)]
316 fn is_suppressed_target_family(pred: &Pred) -> bool {
317 match (pred.key.as_str(), pred.value.as_deref()) {
318 ("target_family", Some("unix")) => true,
319 ("target_family", Some("windows")) => true,
320 _ => false,
321 }
322 }
323}
324
325impl VisitMut<Pred> for SuppressTargetFamily {
326 fn visit_mut_all(&mut self, All(all): &mut All<Pred>) {
327 if all.iter().any(Self::has_specified_family_implier) {
328 all.remove_if(|x| match x {
329 Expr::Var(Var(pred)) => Self::is_suppressed_target_family(pred),
330 Expr::Not(Not(not)) => match &**not {
331 Expr::Var(Var(pred)) => Self::is_suppressed_target_family(pred),
332 _ => false,
333 },
334 _ => false,
335 });
336 }
337
338 walk_mut_expr_list(self, all);
339 }
340}
341
342struct MergePattern;
343
344impl MergePattern {
345 fn merge(any_list: &mut [Expr]) {
346 let mut pattern_list = filter_map_collect_vec(any_list, |x| {
347 if let Expr::All(All(all)) = x {
348 if let [first, second] = all.as_mut_slice() {
349 if first.is_any() || first.is_var() {
350 return Some((first, second));
351 }
352 }
353 }
354 None
355 });
356
357 if let [head, rest @ ..] = pattern_list.as_mut_slice() {
358 let agg = match head.0 {
359 Expr::Any(Any(any)) => any,
360 Expr::Var(var) => {
361 *head.0 = expr(any((var.clone(),)));
362 head.0.as_mut_any().map(|x| &mut x.0).unwrap()
363 }
364 _ => panic!(),
365 };
366
367 for x in rest {
368 let to_agg = if x.1 == head.1 {
369 &mut *x.0
370 } else if x.0 == head.1 {
371 &mut *x.1
372 } else {
373 continue;
374 };
375
376 match mem::replace(to_agg, Expr::Const(false)) {
377 Expr::Any(Any(any)) => agg.extend(any),
378 Expr::Var(var) => agg.push(expr(var.clone())),
379 other => *to_agg = other,
380 }
381 }
382
383 if agg.len() == 1 {
384 *head.0 = agg.pop().unwrap();
385 }
386 }
387 }
388}
389
390impl VisitMut<Pred> for MergePattern {
391 fn visit_mut_any(&mut self, Any(any_list): &mut Any<Pred>) {
392 Self::merge(any_list);
393 Self::merge(&mut any_list[1..]);
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use bool_logic::ast::all;
400 use bool_logic::ast::not;
401 use bool_logic::cfg::ast::target_os;
402 use bool_logic::cfg::ast::target_vendor;
403
404 use super::*;
405
406 #[test]
407 fn sort() {
408 let mut expr = expr(all((not(flag("unix")), flag("unix"))));
409 SortByPriority.visit_mut_expr(&mut expr);
410 assert_eq!(expr.to_string(), "all(unix, not(unix))");
411 }
412
413 #[test]
414 fn suppress_target_family() {
415 let expr = simplified_expr(all((target_os("linux"), flag("unix"))));
417 assert_eq!(expr.to_string(), r#"target_os = "linux""#);
418
419 let expr = simplified_expr(all((target_vendor("apple"), flag("unix"))));
421 assert_eq!(expr.to_string(), r#"target_vendor = "apple""#);
422
423 let expr = simplified_expr(all((target_vendor("unknown"), flag("unix"))));
425 assert_eq!(expr.to_string(), r#"all(unix, target_vendor = "unknown")"#);
426 }
427
428 #[test]
429 fn imply() {
430 {
431 let mut expr = expr(all((target_os("linux"), not(target_os("emscripten")))));
432 ImplyByKey.visit_mut_expr(&mut expr);
433 assert_eq!(expr.to_string(), r#"all(target_os = "linux", not(false))"#);
434 }
435 {
436 let mut expr = expr(all((
437 any((target_os("ios"), target_os("macos"))), any((target_os("linux"), target_os("android"))), )));
440 ImplyByKey.visit_mut_expr(&mut expr);
441 assert_eq!(
442 expr.to_string(),
443 r#"all(any(target_os = "ios", target_os = "macos"), any(false, false))"#
444 );
445 }
446 }
447}