python_ast/ast/tree/
bool_ops.rs1use proc_macro2::TokenStream;
2use pyo3::{FromPyObject, PyAny, PyResult};
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(ob: &'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(ob: &'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 = match op_type.as_ref() {
61 "And" => BoolOps::And,
62 "Or" => BoolOps::Or,
63
64 _ => {
65 log::debug!("Found unknown BoolOp {:?}", op);
66 BoolOps::Unknown
67 }
68 };
69
70 log::debug!(
71 "left: {:?}, right: {:?}, op: {:?}/{:?}",
72 left,
73 right,
74 op_type,
75 op
76 );
77
78 return Ok(BoolOp {
79 op: op,
80 left: Box::new(left),
81 right: Box::new(right),
82 });
83 }
84}
85
86impl<'a> CodeGen for BoolOp {
87 type Context = CodeGenContext;
88 type Options = PythonOptions;
89 type SymbolTable = SymbolTableScopes;
90
91 fn to_rust(
92 self,
93 ctx: Self::Context,
94 options: Self::Options,
95 symbols: Self::SymbolTable,
96 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
97 let left = self
98 .left
99 .clone()
100 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
101 let right = self
102 .right
103 .clone()
104 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
105 match self.op {
106 BoolOps::Or => Ok(quote!((#left) || (#right))),
107 BoolOps::And => Ok(quote!((#left) && (#right))),
108
109 _ => Err(Error::BoolOpNotYetImplemented(self).into()),
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_and() {
120 let options = PythonOptions::default();
121 let result = crate::parse("1 and 2", "test_case.py").unwrap();
122 log::info!("Python tree: {:?}", result);
123 let code = result
126 .to_rust(
127 CodeGenContext::Module("test_case".to_string()),
128 options,
129 SymbolTableScopes::new(),
130 )
131 .unwrap();
132 log::info!("module: {:?}", code);
133 }
134
135 #[test]
136 fn test_or() {
137 let options = PythonOptions::default();
138 let result = crate::parse("1 or 2", "test_case.py").unwrap();
139 log::info!("Python tree: {:?}", result);
140 let code = result
143 .to_rust(
144 CodeGenContext::Module("test_case".to_string()),
145 options,
146 SymbolTableScopes::new(),
147 )
148 .unwrap();
149 log::info!("module: {:?}", code);
150 }
151}