Skip to content

Commit 1c03cb9

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

13 files changed

+394
-121
lines changed

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

+70-3
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ use uv_pep440::{release_specifier_to_range, Operator, Version, VersionSpecifier}
5757
use version_ranges::Ranges;
5858

5959
use crate::marker::lowering::{
60-
LoweredMarkerValueExtra, LoweredMarkerValueString, LoweredMarkerValueVersion,
60+
LoweredMarkerValueExtra, LoweredMarkerValueString, LoweredMarkerValueVersion, Platform,
6161
};
6262
use crate::marker::MarkerValueExtra;
63-
use crate::ExtraOperator;
63+
use crate::{ExtraOperator, MarkerValueString};
6464
use crate::{MarkerExpression, MarkerOperator, MarkerValueVersion};
6565

6666
/// The global node interner.
@@ -260,6 +260,24 @@ impl InternerGuard<'_> {
260260
},
261261
Edges::from_bool(false),
262262
),
263+
// A variable representing the output of a platform key. Edges correspond
264+
// to disjoint platform ranges.
265+
MarkerExpression::String {
266+
key: MarkerValueString::PlatformSystem,
267+
operator,
268+
value,
269+
} => {
270+
let platform = Platform::from_platform_system(value);
271+
(Variable::Platform, Edges::from_platform(operator, platform))
272+
}
273+
MarkerExpression::String {
274+
key: MarkerValueString::SysPlatform,
275+
operator,
276+
value,
277+
} => {
278+
let platform = Platform::from_sys_platform(value);
279+
(Variable::Platform, Edges::from_platform(operator, platform))
280+
}
263281
// A variable representing the output of a string key. Edges correspond
264282
// to disjoint string ranges.
265283
MarkerExpression::String {
@@ -624,6 +642,8 @@ pub(crate) enum Variable {
624642
Version(LoweredMarkerValueVersion),
625643
/// A string marker, such as `os_name`.
626644
String(LoweredMarkerValueString),
645+
/// A platform marker, combining `sys_platform` with `platform_system`.
646+
Platform,
627647
/// A variable representing a `<key> in <value>` expression for a particular
628648
/// string marker and value.
629649
In {
@@ -748,6 +768,11 @@ pub(crate) enum Edges {
748768
String {
749769
edges: SmallVec<(Ranges<String>, NodeId)>,
750770
},
771+
// The edges of a platform variable, representing a disjoint set of ranges that cover
772+
// the output space.
773+
Platform {
774+
edges: SmallVec<(Ranges<Platform>, NodeId)>,
775+
},
751776
// The edges of a boolean variable, representing the values `true` (the `high` child)
752777
// and `false` (the `low` child).
753778
Boolean {
@@ -793,6 +818,24 @@ impl Edges {
793818
}
794819
}
795820

821+
/// Returns the [`Edges`] for a platform expression.
822+
fn from_platform(operator: MarkerOperator, value: Platform) -> Edges {
823+
let range: Ranges<Platform> = match operator {
824+
MarkerOperator::Equal => Ranges::singleton(value),
825+
MarkerOperator::NotEqual => Ranges::singleton(value).complement(),
826+
MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value),
827+
MarkerOperator::GreaterEqual => Ranges::higher_than(value),
828+
MarkerOperator::LessThan => Ranges::strictly_lower_than(value),
829+
MarkerOperator::LessEqual => Ranges::lower_than(value),
830+
MarkerOperator::TildeEqual => unreachable!("string comparisons with ~= are ignored"),
831+
_ => unreachable!("`in` and `contains` are treated as boolean variables"),
832+
};
833+
834+
Edges::Platform {
835+
edges: Edges::from_range(&range),
836+
}
837+
}
838+
796839
/// Returns the [`Edges`] for a version specifier.
797840
fn from_specifier(specifier: VersionSpecifier) -> Edges {
798841
let specifier = release_specifier_to_range(normalize_specifier(specifier));
@@ -901,6 +944,11 @@ impl Edges {
901944
(Edges::String { edges }, Edges::String { edges: right_edges }) => Edges::String {
902945
edges: Edges::apply_ranges(edges, parent, right_edges, right_parent, apply),
903946
},
947+
(Edges::Platform { edges }, Edges::Platform { edges: right_edges }) => {
948+
Edges::Platform {
949+
edges: Edges::apply_ranges(edges, parent, right_edges, right_parent, apply),
950+
}
951+
}
904952
// For boolean variables, we simply merge the low and high edges.
905953
(
906954
Edges::Boolean { high, low },
@@ -1000,6 +1048,9 @@ impl Edges {
10001048
(Edges::String { edges }, Edges::String { edges: right_edges }) => {
10011049
Edges::is_disjoint_ranges(edges, parent, right_edges, right_parent, interner)
10021050
}
1051+
(Edges::Platform { edges }, Edges::Platform { edges: right_edges }) => {
1052+
Edges::is_disjoint_ranges(edges, parent, right_edges, right_parent, interner)
1053+
}
10031054
// For boolean variables, we simply check the low and high edges.
10041055
(
10051056
Edges::Boolean { high, low },
@@ -1065,6 +1116,13 @@ impl Edges {
10651116
.map(|(range, node)| (range, f(node.negate(parent))))
10661117
.collect(),
10671118
},
1119+
Edges::Platform { edges: map } => Edges::Platform {
1120+
edges: map
1121+
.iter()
1122+
.cloned()
1123+
.map(|(range, node)| (range, f(node.negate(parent))))
1124+
.collect(),
1125+
},
10681126
Edges::Boolean { high, low } => Edges::Boolean {
10691127
low: f(low.negate(parent)),
10701128
high: f(high.negate(parent)),
@@ -1081,7 +1139,10 @@ impl Edges {
10811139
Edges::String { edges: map } => {
10821140
Either::Left(Either::Right(map.iter().map(|(_, node)| *node)))
10831141
}
1084-
Edges::Boolean { high, low } => Either::Right([*high, *low].into_iter()),
1142+
Edges::Platform { edges: map } => {
1143+
Either::Right(Either::Left(map.iter().map(|(_, node)| *node)))
1144+
}
1145+
Edges::Boolean { high, low } => Either::Right(Either::Right([*high, *low].into_iter())),
10851146
}
10861147
}
10871148

@@ -1100,6 +1161,12 @@ impl Edges {
11001161
.map(|(range, node)| (range, node.not()))
11011162
.collect(),
11021163
},
1164+
Edges::Platform { edges: map } => Edges::Platform {
1165+
edges: map
1166+
.into_iter()
1167+
.map(|(range, node)| (range, node.not()))
1168+
.collect(),
1169+
},
11031170
Edges::Boolean { high, low } => Edges::Boolean {
11041171
high: high.not(),
11051172
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)