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