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