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