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