python_ast/ast/tree/
bool_ops.rs1use proc_macro2::TokenStream;
2use pyo3::{Bound, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods, types::PyTypeMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 dump, CodeGen, CodeGenContext, Error, ExprType, Node, PythonOptions, SymbolTableScopes,
8};
9
10#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
11pub enum BoolOps {
12 And,
13 Or,
14 Unknown,
15}
16
17impl<'a> FromPyObject<'a> for BoolOps {
18 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
19 let err_msg = format!("Unimplemented unary op {}", dump(ob, None)?);
20 Err(pyo3::exceptions::PyValueError::new_err(
21 ob.error_message("<unknown>", err_msg),
22 ))
23 }
24}
25
26#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
27pub struct BoolOp {
28 op: BoolOps,
29 left: Box<ExprType>,
30 right: Box<ExprType>,
31}
32
33impl<'a> FromPyObject<'a> for BoolOp {
34 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
35 log::debug!("ob: {}", dump(ob, None)?);
36 let op = ob.getattr("op").expect(
37 ob.error_message("<unknown>", "error getting unary operator")
38 .as_str(),
39 );
40
41 let op_type = op.get_type().name().expect(
42 ob.error_message(
43 "<unknown>",
44 format!("extracting type name {:?} for binary operator", op),
45 )
46 .as_str(),
47 );
48
49 let values = ob.getattr("values").expect(
50 ob.error_message("<unknown>", "error getting binary operand")
51 .as_str(),
52 );
53
54 println!("BoolOps values: {}", dump(&values, None)?);
55
56 let value: Vec<ExprType> = values.extract().expect("getting values from BoolOp");
57 let left = value[0].clone();
58 let right = value[1].clone();
59
60 let op_type_str: String = op_type.extract()?;
61 let op = match op_type_str.as_str() {
62 "And" => BoolOps::And,
63 "Or" => BoolOps::Or,
64
65 _ => {
66 log::debug!("Found unknown BoolOp {:?}", op);
67 BoolOps::Unknown
68 }
69 };
70
71 log::debug!(
72 "left: {:?}, right: {:?}, op: {:?}/{:?}",
73 left,
74 right,
75 op_type,
76 op
77 );
78
79 return Ok(BoolOp {
80 op: op,
81 left: Box::new(left),
82 right: Box::new(right),
83 });
84 }
85}
86
87impl<'a> CodeGen for BoolOp {
88 type Context = CodeGenContext;
89 type Options = PythonOptions;
90 type SymbolTable = SymbolTableScopes;
91
92 fn to_rust(
93 self,
94 ctx: Self::Context,
95 options: Self::Options,
96 symbols: Self::SymbolTable,
97 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
98 let left = self
99 .left
100 .clone()
101 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
102 let right = self
103 .right
104 .clone()
105 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
106 match self.op {
107 BoolOps::Or => Ok(quote!((#left) || (#right))),
108 BoolOps::And => Ok(quote!((#left) && (#right))),
109
110 _ => Err(Error::BoolOpNotYetImplemented(self).into()),
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_and() {
121 let options = PythonOptions::default();
122 let result = crate::parse("1 and 2", "test_case.py").unwrap();
123 log::info!("Python tree: {:?}", result);
124 let code = result
127 .to_rust(
128 CodeGenContext::Module("test_case".to_string()),
129 options,
130 SymbolTableScopes::new(),
131 )
132 .unwrap();
133 log::info!("module: {:?}", code);
134 }
135
136 #[test]
137 fn test_or() {
138 let options = PythonOptions::default();
139 let result = crate::parse("1 or 2", "test_case.py").unwrap();
140 log::info!("Python tree: {:?}", result);
141 let code = result
144 .to_rust(
145 CodeGenContext::Module("test_case".to_string()),
146 options,
147 SymbolTableScopes::new(),
148 )
149 .unwrap();
150 log::info!("module: {:?}", code);
151 }
152}