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 op_type = ob.get_type().name().expect(
20 ob.error_message(
21 "<unknown>",
22 format!("extracting type name {:?} for boolean operator", ob),
23 )
24 .as_str(),
25 );
26
27 let op_type_str: String = op_type.extract()?;
28 let op = match op_type_str.as_str() {
29 "And" => BoolOps::And,
30 "Or" => BoolOps::Or,
31 _ => {
32 log::debug!("Found unknown BoolOp {:?}", op_type_str);
33 BoolOps::Unknown
34 }
35 };
36
37 Ok(op)
38 }
39}
40
41#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
42pub struct BoolOp {
43 op: BoolOps,
44 left: Box<ExprType>,
45 right: Box<ExprType>,
46}
47
48impl<'a> FromPyObject<'a> for BoolOp {
49 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
50 log::debug!("ob: {}", dump(ob, None)?);
51 let op = ob.getattr("op").expect(
52 ob.error_message("<unknown>", "error getting unary operator")
53 .as_str(),
54 );
55
56 let op_type = op.get_type().name().expect(
57 ob.error_message(
58 "<unknown>",
59 format!("extracting type name {:?} for binary operator", op),
60 )
61 .as_str(),
62 );
63
64 let values = ob.getattr("values").expect(
65 ob.error_message("<unknown>", "error getting binary operand")
66 .as_str(),
67 );
68
69 log::debug!("BoolOps values: {}", dump(&values, None)?);
70
71 let value: Vec<ExprType> = values.extract().expect("getting values from BoolOp");
72 let left = value[0].clone();
73 let right = value[1].clone();
74
75 let op_type_str: String = op_type.extract()?;
76 let op = match op_type_str.as_str() {
77 "And" => BoolOps::And,
78 "Or" => BoolOps::Or,
79
80 _ => {
81 log::debug!("Found unknown BoolOp {:?}", op);
82 BoolOps::Unknown
83 }
84 };
85
86 log::debug!(
87 "left: {:?}, right: {:?}, op: {:?}/{:?}",
88 left,
89 right,
90 op_type,
91 op
92 );
93
94 return Ok(BoolOp {
95 op: op,
96 left: Box::new(left),
97 right: Box::new(right),
98 });
99 }
100}
101
102impl<'a> CodeGen for BoolOp {
103 type Context = CodeGenContext;
104 type Options = PythonOptions;
105 type SymbolTable = SymbolTableScopes;
106
107 fn to_rust(
108 self,
109 ctx: Self::Context,
110 options: Self::Options,
111 symbols: Self::SymbolTable,
112 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
113 let left = self
114 .left
115 .clone()
116 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
117 let right = self
118 .right
119 .clone()
120 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
121
122 let right_str = right.to_string();
125
126 match self.op {
127 BoolOps::Or => {
128 if right_str.trim() == "None" {
129 Ok(quote!(#left))
132 } else {
133 Ok(quote!((#left) || (#right)))
136 }
137 },
138 BoolOps::And => {
139 Ok(quote!((#left) && (#right)))
142 },
143
144 _ => Err(Error::BoolOpNotYetImplemented(self).into()),
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_and() {
155 let options = PythonOptions::default();
156 let result = crate::parse("1 and 2", "test_case.py").unwrap();
157 log::info!("Python tree: {:?}", result);
158 let code = result
161 .to_rust(
162 CodeGenContext::Module("test_case".to_string()),
163 options,
164 SymbolTableScopes::new(),
165 )
166 .unwrap();
167 log::info!("module: {:?}", code);
168 }
169
170 #[test]
171 fn test_or() {
172 let options = PythonOptions::default();
173 let result = crate::parse("1 or 2", "test_case.py").unwrap();
174 log::info!("Python tree: {:?}", result);
175 let code = result
178 .to_rust(
179 CodeGenContext::Module("test_case".to_string()),
180 options,
181 SymbolTableScopes::new(),
182 )
183 .unwrap();
184 log::info!("module: {:?}", code);
185 }
186}