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}