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}