Skip to content

Commit cfc5550

Browse files
committed
Combine sys_platform and platform_system in marker algebra
1 parent 51ecdd7 commit cfc5550

13 files changed

+381
-125
lines changed

crates/uv-pep508/src/marker/algebra.rs

+68-5
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,9 @@ use std::sync::LazyLock;
5656
use uv_pep440::{release_specifier_to_range, Operator, Version, VersionSpecifier};
5757
use version_ranges::Ranges;
5858

59-
use crate::marker::lowering::{
60-
LoweredMarkerValueExtra, LoweredMarkerValueString, LoweredMarkerValueVersion,
61-
};
59+
use crate::marker::lowering::{LoweredMarkerValueExtra, LoweredMarkerValueString, LoweredMarkerValueVersion, Platform};
6260
use crate::marker::MarkerValueExtra;
63-
use crate::ExtraOperator;
61+
use crate::{ExtraOperator, MarkerValueString};
6462
use crate::{MarkerExpression, MarkerOperator, MarkerValueVersion};
6563

6664
/// The global node interner.
@@ -260,6 +258,24 @@ impl InternerGuard<'_> {
260258
},
261259
Edges::from_bool(false),
262260
),
261+
// A variable representing the output of a platform key. Edges correspond
262+
// to disjoint platform ranges.
263+
MarkerExpression::String {
264+
key: MarkerValueString::PlatformSystem,
265+
operator,
266+
value,
267+
} => {
268+
let platform = Platform::from_platform_system(value);
269+
(Variable::Platform, Edges::from_platform(operator, platform))
270+
}
271+
MarkerExpression::String {
272+
key: MarkerValueString::SysPlatform,
273+
operator,
274+
value,
275+
} => {
276+
let platform = Platform::from_sys_platform(value);
277+
(Variable::Platform, Edges::from_platform(operator, platform))
278+
}
263279
// A variable representing the output of a string key. Edges correspond
264280
// to disjoint string ranges.
265281
MarkerExpression::String {
@@ -624,6 +640,8 @@ pub(crate) enum Variable {
624640
Version(LoweredMarkerValueVersion),
625641
/// A string marker, such as `os_name`.
626642
String(LoweredMarkerValueString),
643+
/// A platform marker, combining `sys_platform` with `platform_system`.
644+
Platform,
627645
/// A variable representing a `<key> in <value>` expression for a particular
628646
/// string marker and value.
629647
In {
@@ -748,6 +766,11 @@ pub(crate) enum Edges {
748766
String {
749767
edges: SmallVec<(Ranges<String>, NodeId)>,
750768
},
769+
// The edges of a platform variable, representing a disjoint set of ranges that cover
770+
// the output space.
771+
Platform {
772+
edges: SmallVec<(Ranges<Platform>, NodeId)>,
773+
},
751774
// The edges of a boolean variable, representing the values `true` (the `high` child)
752775
// and `false` (the `low` child).
753776
Boolean {
@@ -793,6 +816,24 @@ impl Edges {
793816
}
794817
}
795818

819+
/// Returns the [`Edges`] for a platform expression.
820+
fn from_platform(operator: MarkerOperator, value: Platform) -> Edges {
821+
let range: Ranges<Platform> = match operator {
822+
MarkerOperator::Equal => Ranges::singleton(value),
823+
MarkerOperator::NotEqual => Ranges::singleton(value).complement(),
824+
MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value),
825+
MarkerOperator::GreaterEqual => Ranges::higher_than(value),
826+
MarkerOperator::LessThan => Ranges::strictly_lower_than(value),
827+
MarkerOperator::LessEqual => Ranges::lower_than(value),
828+
MarkerOperator::TildeEqual => unreachable!("string comparisons with ~= are ignored"),
829+
_ => unreachable!("`in` and `contains` are treated as boolean variables"),
830+
};
831+
832+
Edges::Platform {
833+
edges: Edges::from_range(&range),
834+
}
835+
}
836+
796837
/// Returns the [`Edges`] for a version specifier.
797838
fn from_specifier(specifier: VersionSpecifier) -> Edges {
798839
let specifier = release_specifier_to_range(normalize_specifier(specifier));
@@ -901,6 +942,9 @@ impl Edges {
901942
(Edges::String { edges }, Edges::String { edges: right_edges }) => Edges::String {
902943
edges: Edges::apply_ranges(edges, parent, right_edges, right_parent, apply),
903944
},
945+
(Edges::Platform { edges }, Edges::Platform { edges: right_edges }) => Edges::Platform {
946+
edges: Edges::apply_ranges(edges, parent, right_edges, right_parent, apply),
947+
},
904948
// For boolean variables, we simply merge the low and high edges.
905949
(
906950
Edges::Boolean { high, low },
@@ -1000,6 +1044,9 @@ impl Edges {
10001044
(Edges::String { edges }, Edges::String { edges: right_edges }) => {
10011045
Edges::is_disjoint_ranges(edges, parent, right_edges, right_parent, interner)
10021046
}
1047+
(Edges::Platform { edges }, Edges::Platform { edges: right_edges }) => {
1048+
Edges::is_disjoint_ranges(edges, parent, right_edges, right_parent, interner)
1049+
}
10031050
// For boolean variables, we simply check the low and high edges.
10041051
(
10051052
Edges::Boolean { high, low },
@@ -1065,6 +1112,13 @@ impl Edges {
10651112
.map(|(range, node)| (range, f(node.negate(parent))))
10661113
.collect(),
10671114
},
1115+
Edges::Platform { edges: map } => Edges::Platform {
1116+
edges: map
1117+
.iter()
1118+
.cloned()
1119+
.map(|(range, node)| (range, f(node.negate(parent))))
1120+
.collect(),
1121+
},
10681122
Edges::Boolean { high, low } => Edges::Boolean {
10691123
low: f(low.negate(parent)),
10701124
high: f(high.negate(parent)),
@@ -1081,7 +1135,10 @@ impl Edges {
10811135
Edges::String { edges: map } => {
10821136
Either::Left(Either::Right(map.iter().map(|(_, node)| *node)))
10831137
}
1084-
Edges::Boolean { high, low } => Either::Right([*high, *low].into_iter()),
1138+
Edges::Platform { edges: map } => {
1139+
Either::Right(Either::Left(map.iter().map(|(_, node)| *node)))
1140+
}
1141+
Edges::Boolean { high, low } => Either::Right(Either::Right([*high, *low].into_iter())),
10851142
}
10861143
}
10871144

@@ -1100,6 +1157,12 @@ impl Edges {
11001157
.map(|(range, node)| (range, node.not()))
11011158
.collect(),
11021159
},
1160+
Edges::Platform { edges: map } => Edges::Platform {
1161+
edges: map
1162+
.into_iter()
1163+
.map(|(range, node)| (range, node.not()))
1164+
.collect(),
1165+
},
11031166
Edges::Boolean { high, low } => Edges::Boolean {
11041167
high: high.not(),
11051168
low: low.not(),

crates/uv-pep508/src/marker/lowering.rs

+62
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,65 @@ impl Display for LoweredMarkerValueExtra {
141141
}
142142
}
143143
}
144+
145+
/// The [`MarkerValue`] value used in `platform` markers. A combination of `sys_platform` and
146+
/// `platform_system`.
147+
///
148+
/// The core idea is that we normalize `platform_system == 'Linux'` to `sys_platform == 'linux'`,
149+
/// `platform_system == 'Windows'` to `sys_platform == 'win32'`, and `platform_system == 'Darwin'`
150+
/// to `sys_platform == 'darwin'`. Apart from that, values are passed through as-is.
151+
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
152+
#[allow(clippy::enum_variant_names)]
153+
pub enum Platform {
154+
Darwin,
155+
Linux,
156+
Windows,
157+
SysPlatform(String),
158+
PlatformSystem(String),
159+
}
160+
161+
impl Platform {
162+
/// Construct a [`Platform`] from the `sys_platform` value.
163+
pub fn from_sys_platform(value: String) -> Self {
164+
match value.as_str() {
165+
"linux" => Self::Linux,
166+
"win32" => Self::Windows,
167+
"darwin" => Self::Darwin,
168+
_ => Self::SysPlatform(value),
169+
}
170+
}
171+
172+
/// Construct a [`Platform`] from the `platform_system` value.
173+
pub fn from_platform_system(value: String) -> Self {
174+
match value.as_str() {
175+
"Linux" => Self::Linux,
176+
"Windows" => Self::Windows,
177+
"Darwin" => Self::Darwin,
178+
_ => Self::PlatformSystem(value),
179+
}
180+
}
181+
}
182+
183+
impl Display for Platform {
184+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
185+
match self {
186+
Platform::Linux => f.write_str("sys_platform == 'linux'"),
187+
Platform::Windows => f.write_str("sys_platform == 'win32'"),
188+
Platform::Darwin => f.write_str("sys_platform == 'darwin'"),
189+
Platform::SysPlatform(platform) => write!(f, "sys_platform == '{platform}'"),
190+
Platform::PlatformSystem(system) => write!(f, "platform_system == '{system}'"),
191+
}
192+
}
193+
}
194+
195+
impl From<Platform> for (MarkerValueString, String) {
196+
fn from(value: Platform) -> Self {
197+
match value {
198+
Platform::Linux => (MarkerValueString::SysPlatform, "linux".to_string()),
199+
Platform::Windows => (MarkerValueString::SysPlatform, "win32".to_string()),
200+
Platform::Darwin => (MarkerValueString::SysPlatform, "darwin".to_string()),
201+
Platform::SysPlatform(value) => (MarkerValueString::SysPlatform, value),
202+
Platform::PlatformSystem(value) => (MarkerValueString::PlatformSystem, value),
203+
}
204+
}
205+
}

crates/uv-pep508/src/marker/simplify.rs

+35
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,41 @@ fn collect_dnf(
120120
}
121121
}
122122
}
123+
MarkerTreeKind::Platform(marker) => {
124+
for (tree, range) in collect_edges(marker.children()) {
125+
// Detect whether the range for this edge can be simplified as an inequality.
126+
if let Some(excluded) = range_inequality(&range) {
127+
let current = path.len();
128+
for value in excluded {
129+
let (key, value) = value.clone().into();
130+
path.push(MarkerExpression::String {
131+
key,
132+
operator: MarkerOperator::NotEqual,
133+
value,
134+
});
135+
}
136+
137+
collect_dnf(&tree, dnf, path);
138+
path.truncate(current);
139+
continue;
140+
}
141+
142+
for bounds in range.iter() {
143+
let current = path.len();
144+
for (operator, value) in MarkerOperator::from_bounds(bounds) {
145+
let (key, value) = value.clone().into();
146+
path.push(MarkerExpression::String {
147+
key,
148+
operator,
149+
value,
150+
});
151+
}
152+
153+
collect_dnf(&tree, dnf, path);
154+
path.truncate(current);
155+
}
156+
}
157+
}
123158
MarkerTreeKind::In(marker) => {
124159
for (value, tree) in marker.children() {
125160
let operator = if value {

0 commit comments

Comments
 (0)