python_ast/ast/tree/
bool_ops.rs

1use 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        //log::info!("{}", result.to_rust().unwrap());
124
125        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        //log::info!("{}", result);
141
142        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}