1use proc_macro2::TokenStream;
2use pyo3::{Bound, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 CodeGen, CodeGenContext, ExprType, Node, PythonOptions, Statement, SymbolTableScopes,
8 extract_list,
9};
10
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
13pub struct Try {
14 pub body: Vec<Statement>,
16 pub handlers: Vec<ExceptHandler>,
18 pub orelse: Vec<Statement>,
20 pub finalbody: Vec<Statement>,
22 pub lineno: Option<usize>,
24 pub col_offset: Option<usize>,
25 pub end_lineno: Option<usize>,
26 pub end_col_offset: Option<usize>,
27}
28
29#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
31pub struct ExceptHandler {
32 pub exception_type: Option<ExprType>,
34 pub name: Option<String>,
36 pub body: Vec<Statement>,
38 pub lineno: Option<usize>,
40 pub col_offset: Option<usize>,
41 pub end_lineno: Option<usize>,
42 pub end_col_offset: Option<usize>,
43}
44
45impl<'a> FromPyObject<'a> for Try {
46 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
47 let body: Vec<Statement> = extract_list(ob, "body", "try body")?;
49
50 let handlers: Vec<ExceptHandler> = extract_list(ob, "handlers", "try handlers")?;
52
53 let orelse: Vec<Statement> = extract_list(ob, "orelse", "try orelse").unwrap_or_default();
55
56 let finalbody: Vec<Statement> = extract_list(ob, "finalbody", "try finalbody").unwrap_or_default();
58
59 Ok(Try {
60 body,
61 handlers,
62 orelse,
63 finalbody,
64 lineno: ob.lineno(),
65 col_offset: ob.col_offset(),
66 end_lineno: ob.end_lineno(),
67 end_col_offset: ob.end_col_offset(),
68 })
69 }
70}
71
72impl<'a> FromPyObject<'a> for ExceptHandler {
73 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
74 let exception_type: Option<ExprType> = if let Ok(type_attr) = ob.getattr("type") {
76 if type_attr.is_none() {
77 None
78 } else {
79 Some(type_attr.extract()?)
80 }
81 } else {
82 None
83 };
84
85 let name: Option<String> = if let Ok(name_attr) = ob.getattr("name") {
87 if name_attr.is_none() {
88 None
89 } else {
90 Some(name_attr.extract()?)
91 }
92 } else {
93 None
94 };
95
96 let body: Vec<Statement> = extract_list(ob, "body", "except handler body")?;
98
99 Ok(ExceptHandler {
100 exception_type,
101 name,
102 body,
103 lineno: ob.lineno(),
104 col_offset: ob.col_offset(),
105 end_lineno: ob.end_lineno(),
106 end_col_offset: ob.end_col_offset(),
107 })
108 }
109}
110
111impl Node for Try {
112 fn lineno(&self) -> Option<usize> { self.lineno }
113 fn col_offset(&self) -> Option<usize> { self.col_offset }
114 fn end_lineno(&self) -> Option<usize> { self.end_lineno }
115 fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
116}
117
118impl Node for ExceptHandler {
119 fn lineno(&self) -> Option<usize> { self.lineno }
120 fn col_offset(&self) -> Option<usize> { self.col_offset }
121 fn end_lineno(&self) -> Option<usize> { self.end_lineno }
122 fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
123}
124
125impl CodeGen for Try {
126 type Context = CodeGenContext;
127 type Options = PythonOptions;
128 type SymbolTable = SymbolTableScopes;
129
130 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
131 let symbols = self.body.into_iter().fold(symbols, |acc, stmt| stmt.find_symbols(acc));
133 let symbols = self.handlers.into_iter().fold(symbols, |acc, handler| {
134 let symbols = handler.body.into_iter().fold(acc, |acc, stmt| stmt.find_symbols(acc));
135 if let Some(exception_type) = handler.exception_type {
136 exception_type.find_symbols(symbols)
137 } else {
138 symbols
139 }
140 });
141 let symbols = self.orelse.into_iter().fold(symbols, |acc, stmt| stmt.find_symbols(acc));
142 self.finalbody.into_iter().fold(symbols, |acc, stmt| stmt.find_symbols(acc))
143 }
144
145 fn to_rust(
146 self,
147 ctx: Self::Context,
148 options: Self::Options,
149 symbols: Self::SymbolTable,
150 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
151 let try_body_tokens: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = self.body.into_iter()
153 .map(|stmt| stmt.to_rust(ctx.clone(), options.clone(), symbols.clone()))
154 .collect();
155 let try_body_tokens = try_body_tokens?;
156
157 let catch_blocks: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = self.handlers.into_iter()
159 .map(|handler| {
160 let handler_body_tokens: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = handler.body.into_iter()
161 .map(|stmt| stmt.to_rust(ctx.clone(), options.clone(), symbols.clone()))
162 .collect();
163 let handler_body_tokens = handler_body_tokens?;
164
165 Ok::<TokenStream, Box<dyn std::error::Error>>(quote! {
168 #(#handler_body_tokens)*
170 })
171 })
172 .collect();
173 let catch_blocks = catch_blocks?;
174
175 let else_tokens = if !self.orelse.is_empty() {
177 let else_body_tokens: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = self.orelse.into_iter()
178 .map(|stmt| stmt.to_rust(ctx.clone(), options.clone(), symbols.clone()))
179 .collect();
180 let else_body_tokens = else_body_tokens?;
181 quote! {
182 #(#else_body_tokens)*
184 }
185 } else {
186 quote!()
187 };
188
189 let finally_tokens = if !self.finalbody.is_empty() {
191 let finally_body_tokens: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = self.finalbody.into_iter()
192 .map(|stmt| stmt.to_rust(ctx.clone(), options.clone(), symbols.clone()))
193 .collect();
194 let finally_body_tokens = finally_body_tokens?;
195 quote! {
196 #(#finally_body_tokens)*
198 }
199 } else {
200 quote!()
201 };
202
203 Ok(quote! {
206 {
207 #(#try_body_tokens)*
210
211 #(#catch_blocks)*
213
214 #else_tokens
216
217 #finally_tokens
219 }
220 })
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 }