4370c169a3b1bea9997ed29fa78efdbf921e0a5f
[power-instruction-analyzer.git] / src / python.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
3
4 #![cfg(feature = "python")]
5
6 use crate::{
7 CarryFlags, ConditionRegister, Instr, InstructionInput, InstructionOutput, OverflowFlags,
8 };
9 use pyo3::{exceptions::ValueError, prelude::*, wrap_pyfunction, PyObjectProtocol};
10 use std::{borrow::Cow, cell::RefCell, fmt};
11
12 trait ToPythonRepr {
13 fn to_python_repr(&self) -> Cow<str> {
14 struct Helper<T>(RefCell<Option<T>>);
15
16 impl<T: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for Helper<T> {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 self.0.borrow_mut().take().unwrap()(f)
19 }
20 }
21
22 impl<T: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result> Helper<T> {
23 fn new(f: T) -> Self {
24 Helper(RefCell::new(Some(f)))
25 }
26 }
27 Cow::Owned(format!(
28 "{}",
29 Helper::new(|f: &mut fmt::Formatter<'_>| -> fmt::Result { self.write(f) })
30 ))
31 }
32 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 f.write_str(&self.to_python_repr())
34 }
35 }
36
37 fn write_list_body_to_python_repr<I: IntoIterator<Item = T>, T: ToPythonRepr>(
38 list: I,
39 f: &mut fmt::Formatter<'_>,
40 separator: &str,
41 ) -> fmt::Result {
42 let mut first = true;
43 for i in list {
44 if first {
45 first = false;
46 } else {
47 f.write_str(separator)?;
48 }
49 i.write(f)?;
50 }
51 Ok(())
52 }
53
54 struct NamedArgPythonRepr<'a> {
55 name: &'a str,
56 value: &'a (dyn ToPythonRepr + 'a),
57 }
58
59 impl ToPythonRepr for NamedArgPythonRepr<'_> {
60 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str(self.name)?;
62 f.write_str("=")?;
63 self.value.write(f)
64 }
65 }
66
67 impl<T: ToPythonRepr> ToPythonRepr for &'_ T {
68 fn to_python_repr(&self) -> Cow<str> {
69 (**self).to_python_repr()
70 }
71 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 (**self).write(f)
73 }
74 }
75
76 impl ToPythonRepr for bool {
77 fn to_python_repr(&self) -> Cow<str> {
78 Cow::Borrowed(match self {
79 true => "True",
80 false => "False",
81 })
82 }
83 }
84
85 impl<T: ToPythonRepr> ToPythonRepr for Option<T> {
86 fn to_python_repr(&self) -> Cow<str> {
87 match self {
88 Some(v) => v.to_python_repr(),
89 None => Cow::Borrowed("None"),
90 }
91 }
92 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 Some(v) => v.write(f),
95 None => f.write_str("None"),
96 }
97 }
98 }
99
100 impl<T: ToPythonRepr> ToPythonRepr for Vec<T> {
101 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 f.write_str("[")?;
103 write_list_body_to_python_repr(self, f, ", ")?;
104 f.write_str("]")
105 }
106 }
107
108 macro_rules! impl_int_to_python_repr {
109 ($($int:ident,)*) => {
110 $(
111 impl ToPythonRepr for $int {
112 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(f, "{}", self)
114 }
115 }
116 )*
117 };
118 }
119
120 impl_int_to_python_repr! {u8, u16, u32, u64, u128, i8, i16, i32, i64, i128,}
121
122 macro_rules! wrap_type {
123 (
124 #[pymodule($m:expr)]
125 // use tt to work around PyO3 bug fixed in PyO3#832
126 #[pyclass $($pyclass_args:tt)?]
127 #[wrapped($value:ident: $wrapped:ident)]
128 #[args $new_args:tt]
129 $(#[$meta:meta])*
130 struct $wrapper:ident {
131 $(
132 #[set=$setter_name:ident]
133 $(#[$field_meta:meta])*
134 $field_name:ident:$field_type:ty,
135 )*
136 }
137 ) => {
138 #[pyclass $($pyclass_args)?]
139 $(#[$meta])*
140 #[derive(Clone)]
141 struct $wrapper {
142 $value: $wrapped,
143 }
144
145 impl<'source> FromPyObject<'source> for $wrapped {
146 fn extract(ob: &'source PyAny) -> PyResult<Self> {
147 Ok(ob.extract::<$wrapper>()?.$value)
148 }
149 }
150
151 impl IntoPy<PyObject> for $wrapped {
152 fn into_py(self, py: Python) -> PyObject {
153 $wrapper { $value: self }.into_py(py)
154 }
155 }
156
157 #[pymethods]
158 impl $wrapper {
159 #[new]
160 #[args $new_args]
161 fn new($($field_name:$field_type),*) -> Self {
162 Self {
163 $value: $wrapped {
164 $($field_name),*
165 }
166 }
167 }
168 $(
169 #[getter]
170 $(#[$field_meta:meta])*
171 fn $field_name(&self) -> $field_type {
172 self.$value.$field_name
173 }
174 #[setter]
175 fn $setter_name(&mut self, $field_name: $field_type) {
176 self.$value.$field_name = $field_name;
177 }
178 )*
179 }
180
181 impl ToPythonRepr for $wrapped {
182 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 f.write_str(concat!(stringify!($wrapped), "("))?;
184 write_list_body_to_python_repr(&[
185 $(
186 NamedArgPythonRepr {
187 name: stringify!($field_name),
188 value: &self.$field_name,
189 },
190 )*
191 ], f, ", ")?;
192 f.write_str(")")
193 }
194 }
195
196 #[pyproto]
197 impl PyObjectProtocol for $wrapper {
198 fn __str__(&self) -> String {
199 serde_json::to_string(&self.$value).unwrap()
200 }
201 fn __repr__(&self) -> String {
202 self.$value.to_python_repr().into_owned()
203 }
204 }
205
206 $m.add_class::<$wrapper>()?;
207 };
208 }
209
210 macro_rules! wrap_instr_fns {
211 (
212 #![pymodule($m:ident)]
213 $(
214 // use tt to work around PyO3 bug fixed in PyO3#832
215 $(#[pyfunction $pyfunction_args:tt])?
216 $(#[$meta:meta])*
217 fn $name:ident(inputs: $inputs:ty) -> $result:ty;
218 )*
219 ) => {
220 $(
221 {
222 #[pyfunction $($pyfunction_args)?]
223 #[text_signature = "(inputs)"]
224 $(#[$meta])*
225 fn $name(inputs: $inputs) -> PyResult<InstructionOutput> {
226 $crate::instr_models::$name(inputs)
227 .map_err(|err| ValueError::py_err(err.to_string()))
228 }
229
230 $m.add_wrapped(wrap_pyfunction!($name))?;
231 }
232 )*
233 };
234 }
235
236 #[pymodule]
237 fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
238 wrap_type! {
239 #[pymodule(m)]
240 #[pyclass(name = OverflowFlags)]
241 #[wrapped(value: OverflowFlags)]
242 #[args(so, ov, ov32)]
243 #[text_signature = "(so, ov, ov32)"]
244 struct PyOverflowFlags {
245 #[set = set_so]
246 so: bool,
247 #[set = set_ov]
248 ov: bool,
249 #[set = set_ov32]
250 ov32: bool,
251 }
252 }
253
254 wrap_type! {
255 #[pymodule(m)]
256 #[pyclass(name = CarryFlags)]
257 #[wrapped(value: CarryFlags)]
258 #[args(ca, ca32)]
259 #[text_signature = "(ca, ca32)"]
260 struct PyCarryFlags {
261 #[set = set_ca]
262 ca: bool,
263 #[set = set_ca32]
264 ca32: bool,
265 }
266 }
267
268 wrap_type! {
269 #[pymodule(m)]
270 #[pyclass(name = ConditionRegister)]
271 #[wrapped(value: ConditionRegister)]
272 #[args(lt, gt, eq, so)]
273 #[text_signature = "(lt, gt, eq, so)"]
274 struct PyConditionRegister {
275 #[set = set_lt]
276 lt: bool,
277 #[set = set_gt]
278 gt: bool,
279 #[set = set_eq]
280 eq: bool,
281 #[set = set_so]
282 so: bool,
283 }
284 }
285
286 wrap_type! {
287 #[pymodule(m)]
288 #[pyclass(name = InstructionInput)]
289 #[wrapped(value: InstructionInput)]
290 #[args(ra="None", rb="None", rc="None", carry="None", overflow="None")]
291 #[text_signature = "(ra, rb, rc, carry, overflow)"]
292 struct PyInstructionInput {
293 #[set = set_ra]
294 ra: Option<u64>,
295 #[set = set_rb]
296 rb: Option<u64>,
297 #[set = set_rc]
298 rc: Option<u64>,
299 #[set = set_carry]
300 carry: Option<CarryFlags>,
301 #[set = set_overflow]
302 overflow: Option<OverflowFlags>,
303 }
304 }
305
306 wrap_type! {
307 #[pymodule(m)]
308 #[pyclass(name = InstructionOutput)]
309 #[wrapped(value: InstructionOutput)]
310 #[args(
311 rt="None",
312 overflow="None",
313 carry="None",
314 cr0="None",
315 cr1="None",
316 cr2="None",
317 cr3="None",
318 cr4="None",
319 cr5="None",
320 cr6="None",
321 cr7="None"
322 )]
323 #[text_signature = "(\
324 rt=None, \
325 overflow=None, \
326 carry=None, \
327 cr0=None, \
328 cr1=None, \
329 cr2=None, \
330 cr3=None, \
331 cr4=None, \
332 cr5=None, \
333 cr6=None, \
334 cr7=None)"
335 ]
336 struct PyInstructionOutput {
337 #[set = set_rt]
338 rt: Option<u64>,
339 #[set = set_overflow]
340 overflow: Option<OverflowFlags>,
341 #[set = set_carry]
342 carry: Option<CarryFlags>,
343 #[set = set_cr0]
344 cr0: Option<ConditionRegister>,
345 #[set = set_cr1]
346 cr1: Option<ConditionRegister>,
347 #[set = set_cr2]
348 cr2: Option<ConditionRegister>,
349 #[set = set_cr3]
350 cr3: Option<ConditionRegister>,
351 #[set = set_cr4]
352 cr4: Option<ConditionRegister>,
353 #[set = set_cr5]
354 cr5: Option<ConditionRegister>,
355 #[set = set_cr6]
356 cr6: Option<ConditionRegister>,
357 #[set = set_cr7]
358 cr7: Option<ConditionRegister>,
359 }
360 }
361
362 m.setattr(
363 "INSTRS",
364 Instr::VALUES
365 .iter()
366 .map(|&instr| instr.name())
367 .collect::<Vec<_>>(),
368 )?;
369
370 wrap_all_instr_fns!(m);
371 Ok(())
372 }