rustpython_vm/
macros.rs

1#[macro_export]
2macro_rules! extend_module {
3    ( $vm:expr, $module:expr, { $($name:expr => $value:expr),* $(,)? }) => {{
4        $(
5            $vm.__module_set_attr($module, $vm.ctx.intern_str($name), $value).unwrap();
6        )*
7    }};
8}
9
10#[macro_export]
11macro_rules! py_class {
12    ( $ctx:expr, $class_name:expr, $class_base:expr, { $($name:tt => $value:expr),* $(,)* }) => {
13        py_class!($ctx, $class_name, $class_base, $crate::types::PyTypeFlags::BASETYPE, { $($name => $value),* })
14    };
15    ( $ctx:expr, $class_name:expr, $class_base:expr, $flags:expr, { $($name:tt => $value:expr),* $(,)* }) => {
16        {
17            #[allow(unused_mut)]
18            let mut slots = $crate::types::PyTypeSlots::heap_default();
19            slots.flags = $flags;
20            $($crate::py_class!(@extract_slots($ctx, &mut slots, $name, $value));)*
21            let py_class = $ctx.new_class(None, $class_name, $class_base, slots);
22            $($crate::py_class!(@extract_attrs($ctx, &py_class, $name, $value));)*
23            py_class
24        }
25    };
26    (@extract_slots($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => {
27        $slots.$slot_name.store(Some($value));
28    };
29    (@extract_slots($ctx:expr, $class:expr, $name:expr, $value:expr)) => {};
30    (@extract_attrs($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => {};
31    (@extract_attrs($ctx:expr, $class:expr, $name:expr, $value:expr)) => {
32        $class.set_attr($name, $value);
33    };
34}
35
36#[macro_export]
37macro_rules! extend_class {
38    ( $ctx:expr, $class:expr, { $($name:expr => $value:expr),* $(,)* }) => {
39        $(
40            $class.set_attr($ctx.intern_str($name), $value.into());
41        )*
42    };
43}
44
45#[macro_export]
46macro_rules! py_namespace {
47    ( $vm:expr, { $($name:expr => $value:expr),* $(,)* }) => {
48        {
49            let namespace = $crate::builtins::PyNamespace::new_ref(&$vm.ctx);
50            let obj = $crate::object::AsObject::as_object(&namespace);
51            $(
52                obj.generic_setattr($vm.ctx.intern_str($name), $crate::function::PySetterValue::Assign($value.into()), $vm).unwrap();
53            )*
54            namespace
55        }
56    }
57}
58
59/// Macro to match on the built-in class of a Python object.
60///
61/// Like `match`, `match_class!` must be exhaustive, so a default arm without
62/// casting is required.
63///
64/// # Examples
65///
66/// ```
67/// use malachite_bigint::ToBigInt;
68/// use num_traits::Zero;
69///
70/// use rustpython_vm::match_class;
71/// use rustpython_vm::builtins::{PyFloat, PyInt};
72/// use rustpython_vm::{PyPayload};
73///
74/// # rustpython_vm::Interpreter::without_stdlib(Default::default()).enter(|vm| {
75/// let obj = PyInt::from(0).into_pyobject(vm);
76/// assert_eq!(
77///     "int",
78///     match_class!(match obj {
79///         PyInt => "int",
80///         PyFloat => "float",
81///         _ => "neither",
82///     })
83/// );
84/// # });
85///
86/// ```
87///
88/// With a binding to the downcasted type:
89///
90/// ```
91/// use malachite_bigint::ToBigInt;
92/// use num_traits::Zero;
93///
94/// use rustpython_vm::match_class;
95/// use rustpython_vm::builtins::{PyFloat, PyInt};
96/// use rustpython_vm::{ PyPayload};
97///
98/// # rustpython_vm::Interpreter::without_stdlib(Default::default()).enter(|vm| {
99/// let obj = PyInt::from(0).into_pyobject(vm);
100///
101/// let int_value = match_class!(match obj {
102///     i @ PyInt => i.as_bigint().clone(),
103///     f @ PyFloat => f.to_f64().to_bigint().unwrap(),
104///     obj => panic!("non-numeric object {:?}", obj),
105/// });
106///
107/// assert!(int_value.is_zero());
108/// # });
109/// ```
110#[macro_export]
111macro_rules! match_class {
112    // The default arm.
113    (match ($obj:expr) { _ => $default:expr $(,)? }) => {
114        $default
115    };
116
117    // The default arm, binding the original object to the specified identifier.
118    (match ($obj:expr) { $binding:ident => $default:expr $(,)? }) => {{
119        #[allow(clippy::redundant_locals)]
120        let $binding = $obj;
121        $default
122    }};
123    (match ($obj:expr) { ref $binding:ident => $default:expr $(,)? }) => {{
124        #[allow(clippy::redundant_locals)]
125        let $binding = &$obj;
126        $default
127    }};
128
129    // An arm taken when the object is an instance of the specified built-in
130    // class and binding the downcasted object to the specified identifier and
131    // the target expression is a block.
132    (match ($obj:expr) { $binding:ident @ $class:ty => $expr:block $($rest:tt)* }) => {
133        $crate::match_class!(match ($obj) { $binding @ $class => ($expr), $($rest)* })
134    };
135    (match ($obj:expr) { ref $binding:ident @ $class:ty => $expr:block $($rest:tt)* }) => {
136        $crate::match_class!(match ($obj) { ref $binding @ $class => ($expr), $($rest)* })
137    };
138
139    // An arm taken when the object is an instance of the specified built-in
140    // class and binding the downcasted object to the specified identifier.
141    (match ($obj:expr) { $binding:ident @ $class:ty => $expr:expr, $($rest:tt)* }) => {
142        match $obj.downcast::<$class>() {
143            Ok($binding) => $expr,
144            Err(_obj) => $crate::match_class!(match (_obj) { $($rest)* }),
145        }
146    };
147    (match ($obj:expr) { ref $binding:ident @ $class:ty => $expr:expr, $($rest:tt)* }) => {
148        match $obj.payload::<$class>() {
149            ::std::option::Option::Some($binding) => $expr,
150            ::std::option::Option::None => $crate::match_class!(match ($obj) { $($rest)* }),
151        }
152    };
153
154    // An arm taken when the object is an instance of the specified built-in
155    // class and the target expression is a block.
156    (match ($obj:expr) { $class:ty => $expr:block $($rest:tt)* }) => {
157        $crate::match_class!(match ($obj) { $class => ($expr), $($rest)* })
158    };
159
160    // An arm taken when the object is an instance of the specified built-in
161    // class.
162    (match ($obj:expr) { $class:ty => $expr:expr, $($rest:tt)* }) => {
163        if $obj.payload_is::<$class>() {
164            $expr
165        } else {
166            $crate::match_class!(match ($obj) { $($rest)* })
167        }
168    };
169
170    // To allow match expressions without parens around the match target
171    (match $($rest:tt)*) => {
172        $crate::match_class!(@parse_match () ($($rest)*))
173    };
174    (@parse_match ($($target:tt)*) ({ $($inner:tt)* })) => {
175        $crate::match_class!(match ($($target)*) { $($inner)* })
176    };
177    (@parse_match ($($target:tt)*) ($next:tt $($rest:tt)*)) => {
178        $crate::match_class!(@parse_match ($($target)* $next) ($($rest)*))
179    };
180}
181
182#[macro_export]
183macro_rules! identifier(
184    ($as_ctx:expr, $name:ident) => {
185        $as_ctx.as_ref().names.$name
186    };
187);
188
189/// Super detailed logging. Might soon overflow your log buffers
190/// Default, this logging is discarded, except when a the `vm-tracing-logging`
191/// build feature is enabled.
192macro_rules! vm_trace {
193    ($($arg:tt)+) => {
194        #[cfg(feature = "vm-tracing-logging")]
195        trace!($($arg)+);
196    }
197}
198
199macro_rules! flame_guard {
200    ($name:expr) => {
201        #[cfg(feature = "flame-it")]
202        let _guard = ::flame::start_guard($name);
203    };
204}
205
206#[macro_export]
207macro_rules! class_or_notimplemented {
208    ($t:ty, $obj:expr) => {{
209        let a: &$crate::PyObject = &*$obj;
210        match $crate::PyObject::downcast_ref::<$t>(&a) {
211            Some(pyref) => pyref,
212            None => return Ok($crate::function::PyArithmeticValue::NotImplemented),
213        }
214    }};
215}
216
217#[macro_export]
218macro_rules! named_function {
219    ($ctx:expr, $module:ident, $func:ident) => {{
220        #[allow(unused_variables)] // weird lint, something to do with paste probably
221        let ctx: &$crate::Context = &$ctx;
222        $crate::__exports::paste::expr! {
223            ctx.new_method_def(
224                stringify!($func),
225                [<$module _ $func>],
226                ::rustpython_vm::function::PyMethodFlags::empty(),
227            )
228            .to_function()
229            .with_module(ctx.intern_str(stringify!($module)).into())
230            .into_ref(ctx)
231        }
232    }};
233}
234
235// can't use PyThreadingConstraint for stuff like this since it's not an auto trait, and
236// therefore we can't add it ad-hoc to a trait object
237cfg_if::cfg_if! {
238    if #[cfg(feature = "threading")] {
239        macro_rules! py_dyn_fn {
240            (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => {
241                dyn Fn($($arg),*) -> $ret + Send + Sync + 'static
242            };
243        }
244    } else {
245        macro_rules! py_dyn_fn {
246            (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => {
247                dyn Fn($($arg),*) -> $ret + 'static
248            };
249        }
250    }
251}