Skip to content

Commit c52677a

Browse files
authored
Merge pull request RustPython#1388 from RustPython/coolreader18/match_class-match-expr
Match an actual match expression in match_class!
2 parents 63271dc + 481b3f5 commit c52677a

12 files changed

+161
-112
lines changed

vm/src/function.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ pub fn single_or_tuple_any<T: PyValue, F: Fn(PyRef<T>) -> PyResult<bool>>(
559559
message: fn(&PyObjectRef) -> String,
560560
vm: &VirtualMachine,
561561
) -> PyResult<bool> {
562-
match_class!(obj,
562+
match_class!(match obj {
563563
obj @ T => predicate(obj),
564564
tuple @ PyTuple => {
565565
for obj in tuple.elements.iter() {
@@ -569,7 +569,7 @@ pub fn single_or_tuple_any<T: PyValue, F: Fn(PyRef<T>) -> PyResult<bool>>(
569569
}
570570
}
571571
Ok(false)
572-
},
573-
obj => Err(vm.new_type_error(message(&obj)))
574-
)
572+
}
573+
obj => Err(vm.new_type_error(message(&obj))),
574+
})
575575
}

vm/src/macros.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,11 @@ macro_rules! py_namespace {
192192
/// let obj = PyInt::new(0).into_ref(&vm).into_object();
193193
/// assert_eq!(
194194
/// "int",
195-
/// match_class!(obj.clone(),
195+
/// match_class!(match obj.clone() {
196196
/// PyInt => "int",
197197
/// PyFloat => "float",
198198
/// _ => "neither",
199-
/// )
199+
/// })
200200
/// );
201201
///
202202
/// ```
@@ -216,45 +216,69 @@ macro_rules! py_namespace {
216216
/// let vm: VirtualMachine = Default::default();
217217
/// let obj = PyInt::new(0).into_ref(&vm).into_object();
218218
///
219-
/// let int_value = match_class!(obj,
219+
/// let int_value = match_class!(match obj {
220220
/// i @ PyInt => i.as_bigint().clone(),
221221
/// f @ PyFloat => f.to_f64().to_bigint().unwrap(),
222222
/// obj => panic!("non-numeric object {}", obj),
223-
/// );
223+
/// });
224224
///
225225
/// assert!(int_value.is_zero());
226226
/// ```
227227
#[macro_export]
228228
macro_rules! match_class {
229229
// The default arm.
230-
($obj:expr, _ => $default:expr $(,)?) => {
230+
(match ($obj:expr) { _ => $default:expr $(,)? }) => {
231231
$default
232232
};
233233

234234
// The default arm, binding the original object to the specified identifier.
235-
($obj:expr, $binding:ident => $default:expr $(,)?) => {{
235+
(match ($obj:expr) { $binding:ident => $default:expr $(,)? }) => {{
236236
let $binding = $obj;
237237
$default
238238
}};
239239

240+
// An arm taken when the object is an instance of the specified built-in
241+
// class and binding the downcasted object to the specified identifier and
242+
// the target expression is a block.
243+
(match ($obj:expr) { $binding:ident @ $class:ty => $expr:block $($rest:tt)* }) => {
244+
$crate::match_class!(match ($obj) { $binding @ $class => ($expr), $($rest)* })
245+
};
246+
240247
// An arm taken when the object is an instance of the specified built-in
241248
// class and binding the downcasted object to the specified identifier.
242-
($obj:expr, $binding:ident @ $class:ty => $expr:expr, $($rest:tt)*) => {
249+
(match ($obj:expr) { $binding:ident @ $class:ty => $expr:expr, $($rest:tt)* }) => {
243250
match $obj.downcast::<$class>() {
244251
Ok($binding) => $expr,
245-
Err(_obj) => $crate::match_class!(_obj, $($rest)*),
252+
Err(_obj) => $crate::match_class!(match (_obj) { $($rest)* }),
246253
}
247254
};
248255

256+
// An arm taken when the object is an instance of the specified built-in
257+
// class and the target expression is a block.
258+
(match ($obj:expr) { $class:ty => $expr:block $($rest:tt)* }) => {
259+
$crate::match_class!(match ($obj) { $class => ($expr), $($rest)* })
260+
};
261+
249262
// An arm taken when the object is an instance of the specified built-in
250263
// class.
251-
($obj:expr, $class:ty => $expr:expr, $($rest:tt)*) => {
264+
(match ($obj:expr) { $class:ty => $expr:expr, $($rest:tt)* }) => {
252265
if $obj.payload_is::<$class>() {
253266
$expr
254267
} else {
255-
$crate::match_class!($obj, $($rest)*)
268+
$crate::match_class!(match ($obj) { $($rest)* })
256269
}
257270
};
271+
272+
// To allow match expressions without parens around the match target
273+
(match $($rest:tt)*) => {
274+
$crate::match_class!(@parse_match () ($($rest)*))
275+
};
276+
(@parse_match ($($target:tt)*) ({ $($inner:tt)* })) => {
277+
$crate::match_class!(match ($($target)*) { $($inner)* })
278+
};
279+
(@parse_match ($($target:tt)*) ($next:tt $($rest:tt)*)) => {
280+
$crate::match_class!(@parse_match ($($target)* $next) ($($rest)*))
281+
};
258282
}
259283

260284
/// Super detailed logging. Might soon overflow your logbuffers

vm/src/obj/objbyteinner.rs

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,21 @@ pub struct PyByteInner {
3737

3838
impl TryFromObject for PyByteInner {
3939
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
40-
match_class!(obj,
41-
42-
i @ PyBytes => Ok(PyByteInner{elements: i.get_value().to_vec()}),
43-
j @ PyByteArray => Ok(PyByteInner{elements: j.inner.borrow().elements.to_vec()}),
44-
k @ PyMemoryView => Ok(PyByteInner{elements: k.get_obj_value().unwrap()}),
40+
match_class!(match obj {
41+
i @ PyBytes => Ok(PyByteInner {
42+
elements: i.get_value().to_vec()
43+
}),
44+
j @ PyByteArray => Ok(PyByteInner {
45+
elements: j.inner.borrow().elements.to_vec()
46+
}),
47+
k @ PyMemoryView => Ok(PyByteInner {
48+
elements: k.get_obj_value().unwrap()
49+
}),
4550
obj => Err(vm.new_type_error(format!(
46-
"a bytes-like object is required, not {}",
47-
obj.class()
48-
)))
49-
)
51+
"a bytes-like object is required, not {}",
52+
obj.class()
53+
))),
54+
})
5055
}
5156
}
5257

@@ -110,28 +115,38 @@ impl ByteInnerNewOptions {
110115
// Only one argument
111116
} else {
112117
let value = if let OptionalArg::Present(ival) = self.val_option {
113-
match_class!(ival.clone(),
118+
match_class!(match ival.clone() {
114119
i @ PyInt => {
115-
let size = objint::get_value(&i.into_object()).to_usize().unwrap();
116-
Ok(vec![0; size])},
117-
_l @ PyString=> {return Err(vm.new_type_error("string argument without an encoding".to_string()));},
120+
let size = objint::get_value(&i.into_object()).to_usize().unwrap();
121+
Ok(vec![0; size])
122+
}
123+
_l @ PyString => {
124+
return Err(
125+
vm.new_type_error("string argument without an encoding".to_string())
126+
);
127+
}
118128
obj => {
119-
let elements = vm.extract_elements(&obj).or_else(|_| {Err(vm.new_type_error(format!(
120-
"cannot convert {} object to bytes", obj.class().name)))});
129+
let elements = vm.extract_elements(&obj).or_else(|_| {
130+
Err(vm.new_type_error(format!(
131+
"cannot convert {} object to bytes",
132+
obj.class().name
133+
)))
134+
});
121135

122136
let mut data_bytes = vec![];
123-
for elem in elements.unwrap(){
137+
for elem in elements.unwrap() {
124138
let v = objint::to_int(vm, &elem, 10)?;
125139
if let Some(i) = v.to_u8() {
126140
data_bytes.push(i);
127141
} else {
128-
return Err(vm.new_value_error("bytes must be in range(0, 256)".to_string()));
129-
}
130-
142+
return Err(vm.new_value_error(
143+
"bytes must be in range(0, 256)".to_string(),
144+
));
131145
}
132-
Ok(data_bytes)
133146
}
134-
)
147+
Ok(data_bytes)
148+
}
149+
})
135150
} else {
136151
Ok(vec![])
137152
};
@@ -451,16 +466,16 @@ impl PyByteInner {
451466

452467
fn setindex(&mut self, int: PyIntRef, object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
453468
if let Some(idx) = self.elements.get_pos(int.as_bigint().to_i32().unwrap()) {
454-
let result = match_class!(object,
455-
i @ PyInt => {
456-
if let Some(value) = i.as_bigint().to_u8() {
457-
Ok(value)
458-
}else{
459-
Err(vm.new_value_error("byte must be in range(0, 256)".to_string()))
469+
let result = match_class!(match object {
470+
i @ PyInt => {
471+
if let Some(value) = i.as_bigint().to_u8() {
472+
Ok(value)
473+
} else {
474+
Err(vm.new_value_error("byte must be in range(0, 256)".to_string()))
475+
}
460476
}
461-
},
462-
_ => {Err(vm.new_type_error("an integer is required".to_string()))}
463-
);
477+
_ => Err(vm.new_type_error("an integer is required".to_string())),
478+
});
464479
let value = result?;
465480
self.elements[idx] = value;
466481
Ok(vm.new_int(value))
@@ -483,13 +498,13 @@ impl PyByteInner {
483498
.map(|obj| u8::try_from_object(vm, obj))
484499
.collect::<PyResult<Vec<_>>>()?)
485500
}
486-
_ => match_class!(object,
487-
i @ PyMemoryView => {
488-
Ok(i.get_obj_value().unwrap())
489-
},
490-
_ => Err(vm.new_index_error(
491-
"can assign only bytes, buffers, or iterables of ints in range(0, 256)"
492-
.to_string()))),
501+
_ => match_class!(match object {
502+
i @ PyMemoryView => Ok(i.get_obj_value().unwrap()),
503+
_ => Err(vm.new_index_error(
504+
"can assign only bytes, buffers, or iterables of ints in range(0, 256)"
505+
.to_string()
506+
)),
507+
}),
493508
};
494509
let items = sec?;
495510
let range = self
@@ -1134,11 +1149,11 @@ impl PyByteInner {
11341149
}
11351150

11361151
pub fn try_as_byte(obj: &PyObjectRef) -> Option<Vec<u8>> {
1137-
match_class!(obj.clone(),
1138-
1139-
i @ PyBytes => Some(i.get_value().to_vec()),
1140-
j @ PyByteArray => Some(j.inner.borrow().elements.to_vec()),
1141-
_ => None)
1152+
match_class!(match obj.clone() {
1153+
i @ PyBytes => Some(i.get_value().to_vec()),
1154+
j @ PyByteArray => Some(j.inner.borrow().elements.to_vec()),
1155+
_ => None,
1156+
})
11421157
}
11431158

11441159
pub trait ByteOr: ToPrimitive {

vm/src/obj/objint.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,10 @@ impl PyInt {
587587
let mut signed = false;
588588
for (key, value) in kwargs.into_iter() {
589589
if key == "signed" {
590-
signed = match_class!(value,
591-
590+
signed = match_class!(match value {
592591
b @ PyInt => !b.as_bigint().is_zero(),
593592
_ => false,
594-
);
593+
});
595594
}
596595
}
597596
let x;
@@ -625,11 +624,10 @@ impl PyInt {
625624
let value = self.as_bigint();
626625
for (key, value) in kwargs.into_iter() {
627626
if key == "signed" {
628-
signed = match_class!(value,
629-
627+
signed = match_class!(match value {
630628
b @ PyInt => !b.as_bigint().is_zero(),
631629
_ => false,
632-
);
630+
});
633631
}
634632
}
635633
if value.sign() == Sign::Minus && !signed {
@@ -744,30 +742,35 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<Big
744742
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()));
745743
}
746744

747-
match_class!(obj.clone(),
745+
match_class!(match obj.clone() {
748746
string @ PyString => {
749747
let s = string.value.as_str().trim();
750748
str_to_int(vm, s, base)
751-
},
749+
}
752750
bytes @ PyBytes => {
753751
let bytes = bytes.get_value();
754752
let s = std::str::from_utf8(bytes)
755753
.map(|s| s.trim())
756754
.map_err(|e| vm.new_value_error(format!("utf8 decode error: {}", e)))?;
757755
str_to_int(vm, s, base)
758-
},
756+
}
759757
obj => {
760758
let method = vm.get_method_or_type_error(obj.clone(), "__int__", || {
761-
format!("int() argument must be a string or a number, not '{}'", obj.class().name)
759+
format!(
760+
"int() argument must be a string or a number, not '{}'",
761+
obj.class().name
762+
)
762763
})?;
763764
let result = vm.invoke(&method, PyFuncArgs::default())?;
764765
match result.payload::<PyInt>() {
765766
Some(int_obj) => Ok(int_obj.as_bigint().clone()),
766767
None => Err(vm.new_type_error(format!(
767-
"TypeError: __int__ returned non-int (type '{}')", result.class().name))),
768+
"TypeError: __int__ returned non-int (type '{}')",
769+
result.class().name
770+
))),
768771
}
769772
}
770-
)
773+
})
771774
}
772775

773776
fn str_to_int(vm: &VirtualMachine, literal: &str, mut base: u32) -> PyResult<BigInt> {

vm/src/obj/objrange.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,13 +445,13 @@ pub enum RangeIndex {
445445

446446
impl TryFromObject for RangeIndex {
447447
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
448-
match_class!(obj,
448+
match_class!(match obj {
449449
i @ PyInt => Ok(RangeIndex::Int(i)),
450450
s @ PySlice => Ok(RangeIndex::Slice(s)),
451451
obj => Err(vm.new_type_error(format!(
452452
"sequence indices be integers or slices, not {}",
453453
obj.class(),
454-
)))
455-
)
454+
))),
455+
})
456456
}
457457
}

vm/src/obj/objsequence.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,17 @@ pub enum SequenceIndex {
168168

169169
impl TryFromObject for SequenceIndex {
170170
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
171-
match_class!(obj,
172-
i @ PyInt => Ok(SequenceIndex::Int(i32::try_from_object(vm, i.into_object())?)),
171+
match_class!(match obj {
172+
i @ PyInt => Ok(SequenceIndex::Int(i32::try_from_object(
173+
vm,
174+
i.into_object()
175+
)?)),
173176
s @ PySlice => Ok(SequenceIndex::Slice(s)),
174177
obj => Err(vm.new_type_error(format!(
175178
"sequence indices be integers or slices, not {}",
176179
obj.class(),
177-
)))
178-
)
180+
))),
181+
})
179182
}
180183
}
181184

@@ -468,14 +471,13 @@ pub fn is_valid_slice_arg(
468471
vm: &VirtualMachine,
469472
) -> Result<Option<BigInt>, PyObjectRef> {
470473
if let OptionalArg::Present(value) = arg {
471-
match_class!(value,
472-
i @ PyInt => Ok(Some(i.as_bigint().clone())),
473-
_obj @ PyNone => Ok(None),
474-
_=> {
475-
Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()))
476-
}
477-
// TODO: check for an __index__ method
478-
)
474+
match_class!(match value {
475+
i @ PyInt => Ok(Some(i.as_bigint().clone())),
476+
_obj @ PyNone => Ok(None),
477+
_ => Err(vm.new_type_error(
478+
"slice indices must be integers or None or have an __index__ method".to_string()
479+
)), // TODO: check for an __index__ method
480+
})
479481
} else {
480482
Ok(None)
481483
}

0 commit comments

Comments
 (0)