cfg_expr/
expr.rs

1pub mod lexer;
2mod parser;
3
4use smallvec::SmallVec;
5use std::ops::Range;
6
7/// A predicate function, used to combine 1 or more predicates
8/// into a single value
9#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
10pub enum Func {
11    /// `not()` with a configuration predicate. It is true if its predicate
12    /// is false and false if its predicate is true.
13    Not,
14    /// `all()` with a comma separated list of configuration predicates. It
15    /// is false if at least one predicate is false. If there are no predicates,
16    /// it is true.
17    ///
18    /// The associated `usize` is the number of predicates inside the `all()`.
19    All(usize),
20    /// `any()` with a comma separated list of configuration predicates. It
21    /// is true if at least one predicate is true. If there are no predicates,
22    /// it is false.
23    ///
24    /// The associated `usize` is the number of predicates inside the `any()`.
25    Any(usize),
26}
27
28use crate::targets as targ;
29
30/// All predicates that pertains to a target, except for `target_feature`
31#[derive(Clone, PartialEq, Eq, Debug)]
32pub enum TargetPredicate {
33    /// [target_abi](https://github.com/rust-lang/rust/issues/80970)
34    Abi(targ::Abi),
35    /// [target_arch](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch)
36    Arch(targ::Arch),
37    /// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian)
38    Endian(targ::Endian),
39    /// [target_env](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env)
40    Env(targ::Env),
41    /// [target_family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family)
42    /// This also applies to the bare [`unix` and `windows`](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows)
43    /// predicates.
44    Family(targ::Family),
45    /// [target_has_atomic](https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic).
46    HasAtomic(targ::HasAtomic),
47    /// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os)
48    Os(targ::Os),
49    /// [panic](https://doc.rust-lang.org/reference/conditional-compilation.html#panic)
50    Panic(targ::Panic),
51    /// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width)
52    PointerWidth(u8),
53    /// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor)
54    Vendor(targ::Vendor),
55}
56
57pub trait TargetMatcher {
58    fn matches(&self, tp: &TargetPredicate) -> bool;
59}
60
61impl TargetMatcher for targ::TargetInfo {
62    fn matches(&self, tp: &TargetPredicate) -> bool {
63        use TargetPredicate::{
64            Abi, Arch, Endian, Env, Family, HasAtomic, Os, Panic, PointerWidth, Vendor,
65        };
66
67        match tp {
68            // The ABI is allowed to be an empty string
69            Abi(abi) => match &self.abi {
70                Some(a) => abi == a,
71                None => abi.0.is_empty(),
72            },
73            Arch(a) => a == &self.arch,
74            Endian(end) => *end == self.endian,
75            // The environment is allowed to be an empty string
76            Env(env) => match &self.env {
77                Some(e) => env == e,
78                None => env.0.is_empty(),
79            },
80            Family(fam) => self.families.contains(fam),
81            HasAtomic(has_atomic) => self.has_atomics.contains(*has_atomic),
82            Os(os) => match &self.os {
83                Some(self_os) => os == self_os,
84                // os = "none" means it should be matched against None. Note that this is different
85                // from "env" above.
86                None => os.as_str() == "none",
87            },
88            PointerWidth(w) => *w == self.pointer_width,
89            Vendor(ven) => match &self.vendor {
90                Some(v) => ven == v,
91                None => ven == &targ::Vendor::unknown,
92            },
93            Panic(panic) => &self.panic == panic,
94        }
95    }
96}
97
98#[cfg(feature = "targets")]
99impl TargetMatcher for target_lexicon::Triple {
100    #[allow(clippy::cognitive_complexity)]
101    #[allow(clippy::match_same_arms)]
102    fn matches(&self, tp: &TargetPredicate) -> bool {
103        use TargetPredicate::{
104            Abi, Arch, Endian, Env, Family, HasAtomic, Os, Panic, PointerWidth, Vendor,
105        };
106        use target_lexicon::*;
107
108        const NUTTX: target_lexicon::Vendor =
109            target_lexicon::Vendor::Custom(target_lexicon::CustomVendor::Static("nuttx"));
110        const RTEMS: target_lexicon::Vendor =
111            target_lexicon::Vendor::Custom(target_lexicon::CustomVendor::Static("rtems"));
112        const WALI: target_lexicon::Vendor =
113            target_lexicon::Vendor::Custom(target_lexicon::CustomVendor::Static("wali"));
114
115        match tp {
116            Abi(_) => {
117                // `target_abi` is unstable. Assume false for this.
118                false
119            }
120            Arch(arch) => {
121                if arch == &targ::Arch::x86 {
122                    matches!(self.architecture, Architecture::X86_32(_))
123                } else if arch == &targ::Arch::wasm32 {
124                    self.architecture == Architecture::Wasm32
125                        || self.architecture == Architecture::Asmjs
126                } else if arch == &targ::Arch::arm {
127                    matches!(self.architecture, Architecture::Arm(_))
128                } else if arch == &targ::Arch::bpf {
129                    self.architecture == Architecture::Bpfeb
130                        || self.architecture == Architecture::Bpfel
131                } else if arch == &targ::Arch::x86_64 {
132                    self.architecture == Architecture::X86_64
133                        || self.architecture == Architecture::X86_64h
134                } else if arch == &targ::Arch::mips32r6 {
135                    matches!(
136                        self.architecture,
137                        Architecture::Mips32(
138                            Mips32Architecture::Mipsisa32r6 | Mips32Architecture::Mipsisa32r6el
139                        )
140                    )
141                } else if arch == &targ::Arch::mips64r6 {
142                    matches!(
143                        self.architecture,
144                        Architecture::Mips64(
145                            Mips64Architecture::Mipsisa64r6 | Mips64Architecture::Mipsisa64r6el
146                        )
147                    )
148                } else if arch == &targ::Arch::amdgpu {
149                    self.architecture == Architecture::AmdGcn
150                } else {
151                    match arch.0.parse::<Architecture>() {
152                        Ok(a) => match (self.architecture, a) {
153                            (Architecture::Aarch64(_), Architecture::Aarch64(_))
154                            | (Architecture::Mips32(_), Architecture::Mips32(_))
155                            | (Architecture::Mips64(_), Architecture::Mips64(_))
156                            | (Architecture::Powerpc64le, Architecture::Powerpc64)
157                            | (Architecture::Riscv32(_), Architecture::Riscv32(_))
158                            | (Architecture::Riscv64(_), Architecture::Riscv64(_))
159                            | (Architecture::Sparcv9, Architecture::Sparc64) => true,
160                            (a, b) => a == b,
161                        },
162                        Err(_) => false,
163                    }
164                }
165            }
166            Endian(end) => match self.architecture.endianness() {
167                Ok(endian) => matches!(
168                    (end, endian),
169                    (crate::targets::Endian::little, Endianness::Little)
170                        | (crate::targets::Endian::big, Endianness::Big)
171                ),
172
173                Err(_) => false,
174            },
175            Env(env) => {
176                // The environment is implied by some operating systems
177                match self.operating_system {
178                    OperatingSystem::Redox => env == &targ::Env::relibc,
179                    OperatingSystem::VxWorks => env == &targ::Env::gnu,
180                    OperatingSystem::Freebsd => env.0.is_empty(),
181                    OperatingSystem::Netbsd => match self.architecture {
182                        Architecture::Arm(ArmArchitecture::Armv6 | ArmArchitecture::Armv7) => {
183                            env.0.is_empty()
184                        }
185                        _ => env.0.is_empty(),
186                    },
187                    OperatingSystem::None_
188                    | OperatingSystem::Cloudabi
189                    | OperatingSystem::Hermit
190                    | OperatingSystem::IOS(_) => match self.environment {
191                        Environment::LinuxKernel => env == &targ::Env::gnu,
192                        _ => env.0.is_empty(),
193                    },
194                    OperatingSystem::WasiP1 => env == &targ::Env::p1,
195                    OperatingSystem::WasiP2 => env == &targ::Env::p2,
196                    OperatingSystem::Wasi => env.0.is_empty() || env == &targ::Env::p1,
197                    _ => {
198                        if env.0.is_empty() {
199                            matches!(
200                                self.environment,
201                                Environment::Unknown
202                                    | Environment::Android
203                                    | Environment::Softfloat
204                                    | Environment::Androideabi
205                                    | Environment::Eabi
206                                    | Environment::Eabihf
207                                    | Environment::Sim
208                                    | Environment::None
209                            )
210                        } else {
211                            match env.0.parse::<Environment>() {
212                                Ok(e) => {
213                                    // Rustc shortens multiple "gnu*" environments to just "gnu"
214                                    if env == &targ::Env::gnu {
215                                        match self.environment {
216                                            Environment::Gnu
217                                            | Environment::Gnuabi64
218                                            | Environment::Gnueabi
219                                            | Environment::Gnuspe
220                                            | Environment::Gnux32
221                                            | Environment::GnuIlp32
222                                            | Environment::Gnueabihf
223                                            | Environment::GnuLlvm => true,
224                                            // Rust 1.49.0 changed all android targets to have the
225                                            // gnu environment
226                                            Environment::Android | Environment::Androideabi
227                                                if self.operating_system
228                                                    == OperatingSystem::Linux =>
229                                            {
230                                                true
231                                            }
232                                            Environment::Kernel => {
233                                                self.operating_system == OperatingSystem::Linux
234                                            }
235                                            _ => self.architecture == Architecture::Avr,
236                                        }
237                                    } else if env == &targ::Env::musl {
238                                        matches!(
239                                            self.environment,
240                                            Environment::Musl
241                                                | Environment::Musleabi
242                                                | Environment::Musleabihf
243                                                | Environment::Muslabi64
244                                        )
245                                    } else if env == &targ::Env::uclibc {
246                                        matches!(
247                                            self.environment,
248                                            Environment::Uclibc
249                                                | Environment::Uclibceabi
250                                                | Environment::Uclibceabihf
251                                        )
252                                    } else if env == &targ::Env::newlib {
253                                        matches!(
254                                            self.operating_system,
255                                            OperatingSystem::Horizon | OperatingSystem::Espidf
256                                        ) || self.vendor == RTEMS
257                                    } else {
258                                        self.environment == e
259                                    }
260                                }
261                                Err(_) => false,
262                            }
263                        }
264                    }
265                }
266            }
267            Family(fam) => {
268                use OperatingSystem::{
269                    Aix, AmdHsa, Bitrig, Cloudabi, Cuda, Darwin, Dragonfly, Emscripten, Espidf,
270                    Freebsd, Fuchsia, Haiku, Hermit, Horizon, Hurd, IOS, Illumos, L4re, Linux,
271                    MacOSX, Nebulet, Netbsd, None_, Openbsd, Redox, Solaris, TvOS, Uefi, Unknown,
272                    VisionOS, VxWorks, Wasi, WasiP1, WasiP2, WatchOS, Windows,
273                };
274
275                match self.operating_system {
276                    AmdHsa | Bitrig | Cloudabi | Cuda | Hermit | Nebulet | None_ | Uefi => false,
277                    Aix
278                    | Darwin(_)
279                    | Dragonfly
280                    | Espidf
281                    | Freebsd
282                    | Fuchsia
283                    | Haiku
284                    | Hurd
285                    | Illumos
286                    | IOS(_)
287                    | L4re
288                    | MacOSX { .. }
289                    | Horizon
290                    | Netbsd
291                    | Openbsd
292                    | Redox
293                    | Solaris
294                    | TvOS(_)
295                    | VisionOS(_)
296                    | VxWorks
297                    | WatchOS(_) => fam == &crate::targets::Family::unix,
298                    Emscripten => {
299                        match self.architecture {
300                            // asmjs, wasm32 and wasm64 are part of both the wasm and unix families
301                            Architecture::Asmjs | Architecture::Wasm32 => {
302                                fam == &crate::targets::Family::wasm
303                                    || fam == &crate::targets::Family::unix
304                            }
305                            _ => false,
306                        }
307                    }
308                    Unknown if self.vendor == NUTTX || self.vendor == RTEMS => {
309                        fam == &crate::targets::Family::unix
310                    }
311                    Unknown => {
312                        // asmjs, wasm32 and wasm64 are part of the wasm family.
313                        match self.architecture {
314                            Architecture::Asmjs | Architecture::Wasm32 | Architecture::Wasm64 => {
315                                fam == &crate::targets::Family::wasm
316                            }
317                            _ => false,
318                        }
319                    }
320                    Linux if self.vendor == WALI => {
321                        fam == &crate::targets::Family::wasm || fam == &crate::targets::Family::unix
322                    }
323                    Linux => {
324                        // The 'kernel' environment is treated specially as not-unix
325                        if self.environment != Environment::Kernel {
326                            fam == &crate::targets::Family::unix
327                        } else {
328                            false
329                        }
330                    }
331                    Wasi | WasiP1 | WasiP2 => fam == &crate::targets::Family::wasm,
332                    Windows => fam == &crate::targets::Family::windows,
333                    // I really dislike non-exhaustive :(
334                    _ => false,
335                }
336            }
337            HasAtomic(_) => {
338                // atomic support depends on both the architecture and the OS. Assume false for
339                // this.
340                false
341            }
342            Os(os) => {
343                if os == &targ::Os::wasi
344                    && matches!(
345                        self.operating_system,
346                        OperatingSystem::WasiP1 | OperatingSystem::WasiP2
347                    )
348                    || (os == &targ::Os::nuttx && self.vendor == NUTTX)
349                    || (os == &targ::Os::rtems && self.vendor == RTEMS)
350                {
351                    return true;
352                }
353
354                match os.0.parse::<OperatingSystem>() {
355                    Ok(o) => match self.environment {
356                        Environment::HermitKernel => os == &targ::Os::hermit,
357                        _ => self.operating_system == o,
358                    },
359                    Err(_) => {
360                        // Handle special case for darwin/macos, where the triple is
361                        // "darwin", but rustc identifies the OS as "macos"
362                        if os == &targ::Os::macos
363                            && matches!(self.operating_system, OperatingSystem::Darwin(_))
364                        {
365                            true
366                        } else {
367                            // For android, the os is still linux, but the environment is android
368                            os == &targ::Os::android
369                                && self.operating_system == OperatingSystem::Linux
370                                && (self.environment == Environment::Android
371                                    || self.environment == Environment::Androideabi)
372                        }
373                    }
374                }
375            }
376            Panic(_) => {
377                // panic support depends on the OS. Assume false for this.
378                false
379            }
380            Vendor(ven) => match ven.0.parse::<target_lexicon::Vendor>() {
381                Ok(v) => {
382                    if self.vendor == v
383                        || ((self.vendor == NUTTX || self.vendor == RTEMS || self.vendor == WALI)
384                            && ven == &targ::Vendor::unknown)
385                    {
386                        true
387                    } else if let target_lexicon::Vendor::Custom(custom) = &self.vendor {
388                        matches!(custom.as_str(), "esp" | "esp32" | "esp32s2" | "esp32s3")
389                            && (v == target_lexicon::Vendor::Espressif
390                                || v == target_lexicon::Vendor::Unknown)
391                    } else {
392                        false
393                    }
394                }
395                Err(_) => false,
396            },
397            PointerWidth(pw) => {
398                // The gnux32 environment is a special case, where it has an
399                // x86_64 architecture, but a 32-bit pointer width
400                if !matches!(
401                    self.environment,
402                    Environment::Gnux32 | Environment::GnuIlp32
403                ) {
404                    *pw == match self.pointer_width() {
405                        Ok(pw) => pw.bits(),
406                        Err(_) => return false,
407                    }
408                } else {
409                    *pw == 32
410                }
411            }
412        }
413    }
414}
415
416impl TargetPredicate {
417    /// Returns true of the predicate matches the specified target
418    ///
419    /// Note that when matching against a [`target_lexicon::Triple`], the
420    /// `has_target_atomic` and `panic` predicates will _always_ return `false`.
421    ///
422    /// ```
423    /// use cfg_expr::{targets::*, expr::TargetPredicate as tp};
424    /// let win = get_builtin_target_by_triple("x86_64-pc-windows-msvc").unwrap();
425    ///
426    /// assert!(
427    ///     tp::Arch(Arch::x86_64).matches(win) &&
428    ///     tp::Endian(Endian::little).matches(win) &&
429    ///     tp::Env(Env::msvc).matches(win) &&
430    ///     tp::Family(Family::windows).matches(win) &&
431    ///     tp::Os(Os::windows).matches(win) &&
432    ///     tp::PointerWidth(64).matches(win) &&
433    ///     tp::Vendor(Vendor::pc).matches(win)
434    /// );
435    /// ```
436    pub fn matches<T>(&self, target: &T) -> bool
437    where
438        T: TargetMatcher,
439    {
440        target.matches(self)
441    }
442}
443
444#[derive(Clone, Debug)]
445pub(crate) enum Which {
446    Abi,
447    Arch,
448    Endian(targ::Endian),
449    Env,
450    Family,
451    Os,
452    HasAtomic(targ::HasAtomic),
453    Panic,
454    PointerWidth(u8),
455    Vendor,
456}
457
458#[derive(Clone, Debug)]
459pub(crate) struct InnerTarget {
460    which: Which,
461    span: Option<Range<usize>>,
462}
463
464/// A single predicate in a `cfg()` expression
465#[derive(Debug, PartialEq, Eq)]
466pub enum Predicate<'a> {
467    /// A target predicate, with the `target_` prefix
468    Target(TargetPredicate),
469    /// Whether rustc's test harness is [enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#test)
470    Test,
471    /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions)
472    /// when compiling without optimizations.
473    DebugAssertions,
474    /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#proc_macro) for
475    /// crates of the `proc_macro` type.
476    ProcMacro,
477    /// A [`feature = "<name>"`](https://doc.rust-lang.org/nightly/cargo/reference/features.html)
478    Feature(&'a str),
479    /// [target_feature](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature)
480    TargetFeature(&'a str),
481    /// A generic bare predicate key that doesn't match one of the known options, eg `cfg(bare)`
482    Flag(&'a str),
483    /// A generic key = "value" predicate that doesn't match one of the known options, eg `cfg(foo = "bar")`
484    KeyValue { key: &'a str, val: &'a str },
485}
486
487#[derive(Clone, Debug)]
488pub(crate) enum InnerPredicate {
489    Target(InnerTarget),
490    Test,
491    DebugAssertions,
492    ProcMacro,
493    Feature(Range<usize>),
494    TargetFeature(Range<usize>),
495    Other {
496        identifier: Range<usize>,
497        value: Option<Range<usize>>,
498    },
499}
500
501impl InnerPredicate {
502    fn to_pred<'a>(&self, s: &'a str) -> Predicate<'a> {
503        use InnerPredicate as IP;
504        use Predicate::{
505            DebugAssertions, Feature, Flag, KeyValue, ProcMacro, Target, TargetFeature, Test,
506        };
507
508        match self {
509            IP::Target(it) => match &it.which {
510                Which::Abi => Target(TargetPredicate::Abi(targ::Abi::new(
511                    s[it.span.clone().unwrap()].to_owned(),
512                ))),
513                Which::Arch => Target(TargetPredicate::Arch(targ::Arch::new(
514                    s[it.span.clone().unwrap()].to_owned(),
515                ))),
516                Which::Os => Target(TargetPredicate::Os(targ::Os::new(
517                    s[it.span.clone().unwrap()].to_owned(),
518                ))),
519                Which::Vendor => Target(TargetPredicate::Vendor(targ::Vendor::new(
520                    s[it.span.clone().unwrap()].to_owned(),
521                ))),
522                Which::Env => Target(TargetPredicate::Env(targ::Env::new(
523                    s[it.span.clone().unwrap()].to_owned(),
524                ))),
525                Which::Family => Target(TargetPredicate::Family(targ::Family::new(
526                    s[it.span.clone().unwrap()].to_owned(),
527                ))),
528                Which::Endian(end) => Target(TargetPredicate::Endian(*end)),
529                Which::HasAtomic(has_atomic) => Target(TargetPredicate::HasAtomic(*has_atomic)),
530                Which::Panic => Target(TargetPredicate::Panic(targ::Panic::new(
531                    s[it.span.clone().unwrap()].to_owned(),
532                ))),
533                Which::PointerWidth(pw) => Target(TargetPredicate::PointerWidth(*pw)),
534            },
535            IP::Test => Test,
536            IP::DebugAssertions => DebugAssertions,
537            IP::ProcMacro => ProcMacro,
538            IP::Feature(rng) => Feature(&s[rng.clone()]),
539            IP::TargetFeature(rng) => TargetFeature(&s[rng.clone()]),
540            IP::Other { identifier, value } => match value {
541                Some(vs) => KeyValue {
542                    key: &s[identifier.clone()],
543                    val: &s[vs.clone()],
544                },
545                None => Flag(&s[identifier.clone()]),
546            },
547        }
548    }
549}
550
551#[derive(Clone, Debug)]
552pub(crate) enum ExprNode {
553    Fn(Func),
554    Predicate(InnerPredicate),
555}
556
557/// A parsed `cfg()` expression that can evaluated
558#[derive(Clone, Debug)]
559pub struct Expression {
560    pub(crate) expr: SmallVec<[ExprNode; 5]>,
561    // We keep the original string around for providing the arbitrary
562    // strings that can make up an expression
563    pub(crate) original: String,
564}
565
566impl Expression {
567    /// An iterator over each predicate in the expression
568    pub fn predicates(&self) -> impl Iterator<Item = Predicate<'_>> {
569        self.expr.iter().filter_map(move |item| match item {
570            ExprNode::Predicate(pred) => {
571                let pred = pred.clone().to_pred(&self.original);
572                Some(pred)
573            }
574            ExprNode::Fn(_) => None,
575        })
576    }
577
578    /// Evaluates the expression, using the provided closure to determine the value of
579    /// each predicate, which are then combined into a final result depending on the
580    /// functions `not()`, `all()`, or `any()` in the expression.
581    ///
582    /// `eval_predicate` typically returns `bool`, but may return any type that implements
583    /// the `Logic` trait.
584    ///
585    /// ## Examples
586    ///
587    /// ```
588    /// use cfg_expr::{targets::*, Expression, Predicate};
589    ///
590    /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
591    ///
592    /// let expr = Expression::parse(r#"all(not(windows), target_env = "musl", any(target_arch = "x86", target_arch = "x86_64"))"#).unwrap();
593    ///
594    /// assert!(expr.eval(|pred| {
595    ///     match pred {
596    ///         Predicate::Target(tp) => tp.matches(linux_musl),
597    ///         _ => false,
598    ///     }
599    /// }));
600    /// ```
601    ///
602    /// Returning `Option<bool>`, where `None` indicates the result is unknown:
603    ///
604    /// ```
605    /// use cfg_expr::{targets::*, Expression, Predicate};
606    ///
607    /// let expr = Expression::parse(r#"any(target_feature = "sse2", target_env = "musl")"#).unwrap();
608    ///
609    /// let linux_gnu = get_builtin_target_by_triple("x86_64-unknown-linux-gnu").unwrap();
610    /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
611    ///
612    /// fn eval(expr: &Expression, target: &TargetInfo) -> Option<bool> {
613    ///     expr.eval(|pred| {
614    ///         match pred {
615    ///             Predicate::Target(tp) => Some(tp.matches(target)),
616    ///             Predicate::TargetFeature(_) => None,
617    ///             _ => panic!("unexpected predicate"),
618    ///         }
619    ///     })
620    /// }
621    ///
622    /// // Whether the target feature is present is unknown, so the whole expression evaluates to
623    /// // None (unknown).
624    /// assert_eq!(eval(&expr, linux_gnu), None);
625    ///
626    /// // Whether the target feature is present is irrelevant for musl, since the any() always
627    /// // evaluates to true.
628    /// assert_eq!(eval(&expr, linux_musl), Some(true));
629    /// ```
630    pub fn eval<EP, T>(&self, mut eval_predicate: EP) -> T
631    where
632        EP: FnMut(&Predicate<'_>) -> T,
633        T: Logic + std::fmt::Debug,
634    {
635        let mut result_stack = SmallVec::<[T; 8]>::new();
636
637        // We store the expression as postfix, so just evaluate each component
638        // requirement in the order it comes, and then combining the previous
639        // results according to each operator as it comes
640        for node in self.expr.iter() {
641            match node {
642                ExprNode::Predicate(pred) => {
643                    let pred = pred.to_pred(&self.original);
644
645                    result_stack.push(eval_predicate(&pred));
646                }
647                ExprNode::Fn(Func::All(count)) => {
648                    // all() with a comma separated list of configuration predicates.
649                    let mut result = T::top();
650
651                    for _ in 0..*count {
652                        let r = result_stack.pop().unwrap();
653                        result = result.and(r);
654                    }
655
656                    result_stack.push(result);
657                }
658                ExprNode::Fn(Func::Any(count)) => {
659                    // any() with a comma separated list of configuration predicates.
660                    let mut result = T::bottom();
661
662                    for _ in 0..*count {
663                        let r = result_stack.pop().unwrap();
664                        result = result.or(r);
665                    }
666
667                    result_stack.push(result);
668                }
669                ExprNode::Fn(Func::Not) => {
670                    // not() with a configuration predicate.
671                    // It is true if its predicate is false
672                    // and false if its predicate is true.
673                    let r = result_stack.pop().unwrap();
674                    result_stack.push(r.not());
675                }
676            }
677        }
678
679        result_stack.pop().unwrap()
680    }
681
682    /// The original string which has been parsed to produce this [`Expression`].
683    ///
684    /// ```
685    /// use cfg_expr::Expression;
686    ///
687    /// assert_eq!(
688    ///     Expression::parse("any()").unwrap().original(),
689    ///     "any()"
690    /// );
691    /// ```
692    #[inline]
693    pub fn original(&self) -> &str {
694        &self.original
695    }
696}
697
698/// [`PartialEq`] will do a **syntactical** comparison, so will just check if both
699/// expressions have been parsed from the same string, **not** if they are semantically
700/// equivalent.
701///
702/// ```
703/// use cfg_expr::Expression;
704///
705/// assert_eq!(
706///     Expression::parse("any()").unwrap(),
707///     Expression::parse("any()").unwrap()
708/// );
709/// assert_ne!(
710///     Expression::parse("any()").unwrap(),
711///     Expression::parse("unix").unwrap()
712/// );
713/// ```
714impl PartialEq for Expression {
715    fn eq(&self, other: &Self) -> bool {
716        self.original.eq(&other.original)
717    }
718}
719
720/// A propositional logic used to evaluate `Expression` instances.
721///
722/// An `Expression` consists of some predicates and the `any`, `all` and `not` operators. An
723/// implementation of `Logic` defines how the `any`, `all` and `not` operators should be evaluated.
724pub trait Logic {
725    /// The result of an `all` operation with no operands, akin to Boolean `true`.
726    fn top() -> Self;
727
728    /// The result of an `any` operation with no operands, akin to Boolean `false`.
729    fn bottom() -> Self;
730
731    /// `AND`, which corresponds to the `all` operator.
732    fn and(self, other: Self) -> Self;
733
734    /// `OR`, which corresponds to the `any` operator.
735    fn or(self, other: Self) -> Self;
736
737    /// `NOT`, which corresponds to the `not` operator.
738    fn not(self) -> Self;
739}
740
741/// A boolean logic.
742impl Logic for bool {
743    #[inline]
744    fn top() -> Self {
745        true
746    }
747
748    #[inline]
749    fn bottom() -> Self {
750        false
751    }
752
753    #[inline]
754    fn and(self, other: Self) -> Self {
755        self && other
756    }
757
758    #[inline]
759    fn or(self, other: Self) -> Self {
760        self || other
761    }
762
763    #[inline]
764    fn not(self) -> Self {
765        !self
766    }
767}
768
769/// A three-valued logic -- `None` stands for the value being unknown.
770///
771/// The truth tables for this logic are described on
772/// [Wikipedia](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
773impl Logic for Option<bool> {
774    #[inline]
775    fn top() -> Self {
776        Some(true)
777    }
778
779    #[inline]
780    fn bottom() -> Self {
781        Some(false)
782    }
783
784    #[inline]
785    fn and(self, other: Self) -> Self {
786        match (self, other) {
787            // If either is false, the expression is false.
788            (Some(false), _) | (_, Some(false)) => Some(false),
789            // If both are true, the expression is true.
790            (Some(true), Some(true)) => Some(true),
791            // One or both are unknown -- the result is unknown.
792            _ => None,
793        }
794    }
795
796    #[inline]
797    fn or(self, other: Self) -> Self {
798        match (self, other) {
799            // If either is true, the expression is true.
800            (Some(true), _) | (_, Some(true)) => Some(true),
801            // If both are false, the expression is false.
802            (Some(false), Some(false)) => Some(false),
803            // One or both are unknown -- the result is unknown.
804            _ => None,
805        }
806    }
807
808    #[inline]
809    fn not(self) -> Self {
810        self.map(|v| !v)
811    }
812}