1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods, types::PyTypeMethods};
3use quote::quote;
4
5use crate::{
6 dump, err_from, extraction_failure, Assign, AsyncFor, AsyncWith, AugAssign, Call, ClassDef,
7 CodeGen, CodeGenContext, Expr, ExprType, For, FunctionDef, If, Import, ImportFrom, Node,
8 PythonOptions, Raise, StatementNotYetImplemented, SymbolTableScopes, Try, While, With,
9};
10
11use tracing::debug;
12
13use serde::{Deserialize, Serialize};
14
15pub trait PyStatementTrait: Clone + PartialEq {
17}
18
19#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
20pub struct Statement {
21 pub lineno: Option<usize>,
22 pub col_offset: Option<usize>,
23 pub end_lineno: Option<usize>,
24 pub end_col_offset: Option<usize>,
25 pub statement: StatementType,
26}
27
28impl<'a, 'py> FromPyObject<'a, 'py> for Statement {
29 type Error = pyo3::PyErr;
30 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
31 Ok(Self {
32 lineno: ob.lineno(),
33 col_offset: ob.col_offset(),
34 end_lineno: ob.end_lineno(),
35 end_col_offset: ob.end_col_offset(),
36 statement: StatementType::extract(ob)?,
37 })
38 }
39}
40
41impl Node for Statement {
42 fn lineno(&self) -> Option<usize> {
43 self.lineno
44 }
45 fn col_offset(&self) -> Option<usize> {
46 self.col_offset
47 }
48 fn end_lineno(&self) -> Option<usize> {
49 self.end_lineno
50 }
51 fn end_col_offset(&self) -> Option<usize> {
52 self.end_col_offset
53 }
54}
55
56impl CodeGen for Statement {
57 type Context = CodeGenContext;
58 type Options = PythonOptions;
59 type SymbolTable = SymbolTableScopes;
60
61 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
62 self.statement.clone().find_symbols(symbols)
63 }
64
65 fn to_rust(
66 self,
67 ctx: Self::Context,
68 options: Self::Options,
69 symbols: Self::SymbolTable,
70 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
71 let (lineno, col_offset) = (self.lineno, self.col_offset);
72 let (end_lineno, end_col_offset) = (self.end_lineno, self.end_col_offset);
73 self.statement
74 .clone()
75 .to_rust(ctx, options, symbols)
76 .map_err(|e| {
77 let location = crate::SourceLocation::with_span(
78 "<module>",
79 lineno,
80 col_offset.map(|c| c + 1),
81 end_lineno,
82 end_col_offset,
83 );
84 Box::<dyn std::error::Error>::from(crate::codegen_error(
85 location,
86 crate::format_error_chain(e.as_ref()),
87 "",
88 ))
89 })
90 }
91}
92
93#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
94pub enum StatementType {
95 AsyncFunctionDef(FunctionDef),
96 Assert {
97 test: Box<ExprType>,
98 msg: Option<Box<ExprType>>,
99 },
100 Assign(Assign),
101 AugAssign(AugAssign),
102 Break,
103 Continue,
104 ClassDef(ClassDef),
105 Call(Call),
106 Pass,
107 Return(Option<Expr>),
108 Import(Import),
109 ImportFrom(ImportFrom),
110 Expr(Expr),
111 FunctionDef(FunctionDef),
112 If(If),
113 For(For),
114 While(While),
115 Try(Try),
116 AsyncWith(AsyncWith),
117 AsyncFor(AsyncFor),
118 Raise(Raise),
119 With(With),
120
121 Unimplemented(String),
122}
123
124impl<'a, 'py> FromPyObject<'a, 'py> for StatementType {
125 type Error = pyo3::PyErr;
126 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
127 let ob_type = ob
128 .get_type()
129 .name()
130 .map_err(|e| extraction_failure("statement type", &ob, e))?;
131
132 debug!("statement...ob_type: {}...{}", ob_type, dump(&ob, Some(4))?);
133 match ob_type.extract::<String>()?.as_str() {
134 "AsyncFunctionDef" => Ok(StatementType::AsyncFunctionDef(
135 FunctionDef::extract(ob)
136 .map_err(|e| extraction_failure("async function definition", &ob, e))?,
137 )),
138 "Assign" => {
139 let assignment = Assign::extract(ob)
140 .map_err(|e| extraction_failure("assignment", &ob, e))?;
141 Ok(StatementType::Assign(assignment))
142 }
143 "AnnAssign" => {
144 let value = ob
148 .getattr("value")
149 .map_err(|e| extraction_failure("annotated assignment value", &ob, e))?;
150 if value.is_none() {
151 return Ok(StatementType::Pass);
152 }
153 let target = ob
154 .getattr("target")
155 .map_err(|e| extraction_failure("annotated assignment target", &ob, e))?
156 .extract()
157 .map_err(|e| extraction_failure("annotated assignment target", &ob, e))?;
158 let value = value
159 .extract()
160 .map_err(|e| extraction_failure("annotated assignment value", &ob, e))?;
161 Ok(StatementType::Assign(Assign {
162 targets: vec![target],
163 value,
164 type_comment: None,
165 }))
166 }
167 "AugAssign" => {
168 let aug_assignment = AugAssign::extract(ob)
169 .map_err(|e| extraction_failure("augmented assignment", &ob, e))?;
170 Ok(StatementType::AugAssign(aug_assignment))
171 }
172 "Assert" => {
173 let test: ExprType = ob
174 .getattr("test")
175 .map_err(|e| extraction_failure("assert condition", &ob, e))?
176 .extract()
177 .map_err(|e| extraction_failure("assert condition", &ob, e))?;
178 let msg: Option<Box<ExprType>> = match ob.getattr("msg") {
179 Ok(m) if !m.is_none() => Some(Box::new(
180 m.extract()
181 .map_err(|e| extraction_failure("assert message", &ob, e))?,
182 )),
183 _ => None,
184 };
185 Ok(StatementType::Assert {
186 test: Box::new(test),
187 msg,
188 })
189 }
190 "Pass" => Ok(StatementType::Pass),
191 "Call" => {
192 let value = ob
193 .getattr("value")
194 .map_err(|e| extraction_failure("call statement value", &ob, e))?;
195 let call = Call::extract(value.as_borrowed())
196 .map_err(|e| extraction_failure("call statement", &ob, e))?;
197 debug!("call: {:?}", call);
198 Ok(StatementType::Call(call))
199 }
200 "ClassDef" => Ok(StatementType::ClassDef(
201 ClassDef::extract(ob)
202 .map_err(|e| extraction_failure("class definition", &ob, e))?,
203 )),
204 "Continue" => Ok(StatementType::Continue),
205 "Break" => Ok(StatementType::Break),
206 "FunctionDef" => Ok(StatementType::FunctionDef(
207 FunctionDef::extract(ob)
208 .map_err(|e| extraction_failure("function definition", &ob, e))?,
209 )),
210 "Import" => Ok(StatementType::Import(
211 Import::extract(ob).map_err(|e| extraction_failure("import", &ob, e))?,
212 )),
213 "ImportFrom" => Ok(StatementType::ImportFrom(
214 ImportFrom::extract(ob)
215 .map_err(|e| extraction_failure("from-import", &ob, e))?,
216 )),
217 "Expr" => {
218 let expr = ob
219 .extract()
220 .map_err(|e| extraction_failure("expression statement", &ob, e))?;
221 Ok(StatementType::Expr(expr))
222 }
223 "Return" => {
224 tracing::debug!("return expression: {}", dump(&ob, None)?);
225 let return_value = if let Ok(value_attr) = ob.getattr("value") {
227 if value_attr.is_none() {
228 Some(Expr {
230 value: crate::tree::ExprType::NoneType(crate::tree::Constant(None)),
231 ctx: None,
232 lineno: ob.lineno(),
233 col_offset: ob.col_offset(),
234 end_lineno: ob.end_lineno(),
235 end_col_offset: ob.end_col_offset(),
236 })
237 } else {
238 let expr_value: crate::tree::ExprType = value_attr
240 .extract()
241 .map_err(|e| extraction_failure("return value", &ob, e))?;
242 Some(Expr {
243 value: expr_value,
244 ctx: None,
245 lineno: ob.lineno(),
246 col_offset: ob.col_offset(),
247 end_lineno: ob.end_lineno(),
248 end_col_offset: ob.end_col_offset(),
249 })
250 }
251 } else {
252 None
253 };
254 Ok(StatementType::Return(return_value))
255 }
256 "If" => {
257 let if_stmt =
258 If::extract(ob).map_err(|e| extraction_failure("if statement", &ob, e))?;
259 Ok(StatementType::If(if_stmt))
260 }
261 "For" => {
262 let for_stmt =
263 For::extract(ob).map_err(|e| extraction_failure("for loop", &ob, e))?;
264 Ok(StatementType::For(for_stmt))
265 }
266 "While" => {
267 let while_stmt =
268 While::extract(ob).map_err(|e| extraction_failure("while loop", &ob, e))?;
269 Ok(StatementType::While(while_stmt))
270 }
271 "Try" => {
272 let try_stmt =
273 Try::extract(ob).map_err(|e| extraction_failure("try statement", &ob, e))?;
274 Ok(StatementType::Try(try_stmt))
275 }
276 "AsyncWith" => {
277 let async_with_stmt = AsyncWith::extract(ob)
278 .map_err(|e| extraction_failure("async with statement", &ob, e))?;
279 Ok(StatementType::AsyncWith(async_with_stmt))
280 }
281 "AsyncFor" => {
282 let async_for_stmt = AsyncFor::extract(ob)
283 .map_err(|e| extraction_failure("async for loop", &ob, e))?;
284 Ok(StatementType::AsyncFor(async_for_stmt))
285 }
286 "Raise" => {
287 let raise_stmt =
288 Raise::extract(ob).map_err(|e| extraction_failure("raise statement", &ob, e))?;
289 Ok(StatementType::Raise(raise_stmt))
290 }
291 "With" => {
292 let with_stmt =
293 With::extract(ob).map_err(|e| extraction_failure("with statement", &ob, e))?;
294 Ok(StatementType::With(with_stmt))
295 }
296 other => Err(extraction_failure(
297 "statement",
298 &ob,
299 format!(
300 "the `{}` statement is not yet supported by rython",
301 other
302 ),
303 )),
304 }
305 }
306}
307
308fn return_tokens(ctx: &CodeGenContext, value: TokenStream) -> TokenStream {
312 if ctx.in_try_block() {
313 quote!(return Ok(PyFlow::Return(#value)))
314 } else {
315 quote!(return Ok(#value))
316 }
317}
318
319pub fn body_breaks_outward(body: &[Statement]) -> bool {
330 body.iter().any(|stmt| match &stmt.statement {
331 StatementType::Break | StatementType::Continue => true,
332 StatementType::If(s) => {
333 body_breaks_outward(&s.body) || body_breaks_outward(&s.orelse)
334 }
335 StatementType::For(s) => body_breaks_outward(&s.orelse),
338 StatementType::While(s) => body_breaks_outward(&s.orelse),
339 StatementType::AsyncFor(s) => body_breaks_outward(&s.orelse),
340 StatementType::Try(s) => {
341 body_breaks_outward(&s.body)
342 || s.handlers.iter().any(|h| body_breaks_outward(&h.body))
343 || body_breaks_outward(&s.orelse)
344 || body_breaks_outward(&s.finalbody)
345 }
346 StatementType::With(s) => body_breaks_outward(&s.body),
347 StatementType::AsyncWith(s) => body_breaks_outward(&s.body),
348 _ => false,
349 })
350}
351
352pub fn body_contains_function_return(body: &[Statement]) -> bool {
353 body.iter().any(|stmt| match &stmt.statement {
354 StatementType::Return(_) => true,
355 StatementType::If(s) => {
356 body_contains_function_return(&s.body) || body_contains_function_return(&s.orelse)
357 }
358 StatementType::For(s) => {
359 body_contains_function_return(&s.body) || body_contains_function_return(&s.orelse)
360 }
361 StatementType::While(s) => {
362 body_contains_function_return(&s.body) || body_contains_function_return(&s.orelse)
363 }
364 StatementType::AsyncFor(s) => {
365 body_contains_function_return(&s.body) || body_contains_function_return(&s.orelse)
366 }
367 StatementType::Try(s) => {
368 body_contains_function_return(&s.body)
369 || s.handlers
370 .iter()
371 .any(|h| body_contains_function_return(&h.body))
372 || body_contains_function_return(&s.orelse)
373 || body_contains_function_return(&s.finalbody)
374 }
375 StatementType::With(s) => body_contains_function_return(&s.body),
376 StatementType::AsyncWith(s) => body_contains_function_return(&s.body),
377 _ => false,
378 })
379}
380
381pub fn loop_body_has_direct_break(body: &[Statement]) -> bool {
386 body.iter().any(|stmt| match &stmt.statement {
387 StatementType::Break => true,
388 StatementType::If(s) => {
389 loop_body_has_direct_break(&s.body) || loop_body_has_direct_break(&s.orelse)
390 }
391 StatementType::Try(s) => {
392 loop_body_has_direct_break(&s.body)
393 || s.handlers
394 .iter()
395 .any(|h| loop_body_has_direct_break(&h.body))
396 || loop_body_has_direct_break(&s.orelse)
397 || loop_body_has_direct_break(&s.finalbody)
398 }
399 StatementType::With(s) => loop_body_has_direct_break(&s.body),
400 StatementType::AsyncWith(s) => loop_body_has_direct_break(&s.body),
401 _ => false,
402 })
403}
404
405impl CodeGen for StatementType {
406 type Context = CodeGenContext;
407 type Options = PythonOptions;
408 type SymbolTable = SymbolTableScopes;
409
410 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
411 match self {
412 StatementType::Assign(a) => a.find_symbols(symbols),
413 StatementType::AugAssign(a) => a.find_symbols(symbols),
414 StatementType::ClassDef(c) => c.find_symbols(symbols),
415 StatementType::FunctionDef(f) => f.find_symbols(symbols),
416 StatementType::AsyncFunctionDef(f) => f.find_symbols(symbols),
419 StatementType::Import(i) => i.find_symbols(symbols),
420 StatementType::ImportFrom(i) => i.find_symbols(symbols),
421 StatementType::Expr(e) => e.find_symbols(symbols),
422 StatementType::If(i) => i.find_symbols(symbols),
423 StatementType::For(f) => f.find_symbols(symbols),
424 StatementType::While(w) => w.find_symbols(symbols),
425 StatementType::Try(t) => t.find_symbols(symbols),
426 StatementType::AsyncWith(aw) => aw.find_symbols(symbols),
427 StatementType::AsyncFor(af) => af.find_symbols(symbols),
428 StatementType::Raise(r) => r.find_symbols(symbols),
429 StatementType::With(w) => w.find_symbols(symbols),
430 _ => symbols,
431 }
432 }
433
434 fn to_rust(
435 self,
436 ctx: Self::Context,
437 options: Self::Options,
438 symbols: Self::SymbolTable,
439 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
440 match self {
441 StatementType::AsyncFunctionDef(s) => {
442 let func_def = s.to_rust(Self::Context::Async(Box::new(ctx)), options, symbols)?;
443 Ok(quote!(#func_def))
444 }
445 StatementType::Assert { test, msg } => {
446 let test_tokens =
447 crate::condition_to_rust(&test, ctx.clone(), options.clone(), symbols.clone())?;
448 let msg_tokens = match msg {
449 Some(m) => {
450 let m = m.to_rust(ctx.clone(), options, symbols)?;
451 quote!(format!("{}", #m))
452 }
453 None => quote!(String::new()),
454 };
455 Ok(quote! {
460 if !(#test_tokens) {
461 return Err(PyException::new("AssertionError", #msg_tokens));
462 }
463 })
464 }
465 StatementType::Assign(a) => a.to_rust(ctx, options, symbols),
466 StatementType::AugAssign(a) => a.to_rust(ctx, options, symbols),
467 StatementType::Break => {
468 if ctx.break_crosses_try_closure() {
473 return Ok(if ctx.break_target_has_else() {
474 quote! {{ __rython_broke = true; return Ok(PyFlow::Break); }}
475 } else {
476 quote! {return Ok(PyFlow::Break);}
477 });
478 }
479 if matches!(ctx, Self::Context::Loop { has_else: true, .. }) {
482 Ok(quote! {{ __rython_broke = true; break; }})
483 } else {
484 Ok(quote! {break;})
485 }
486 }
487 StatementType::Call(c) => c.to_rust(ctx, options, symbols),
488 StatementType::ClassDef(c) => c.to_rust(ctx, options, symbols),
489 StatementType::Continue => {
490 if ctx.break_crosses_try_closure() {
491 Ok(quote! {return Ok(PyFlow::Continue);})
492 } else {
493 Ok(quote! {continue;})
494 }
495 }
496 StatementType::Pass => Ok(quote! {}),
497 StatementType::FunctionDef(s) => s.to_rust(ctx, options, symbols),
498 StatementType::Import(s) => s.to_rust(ctx, options, symbols),
499 StatementType::ImportFrom(s) => s.to_rust(ctx, options, symbols),
500 StatementType::Expr(s) => s.to_rust(ctx, options, symbols),
501 StatementType::Return(None) => Ok(return_tokens(&ctx, quote!(()))),
508 StatementType::Return(Some(e)) => {
509 let value = if matches!(e.value, ExprType::NoneType(_)) {
510 quote!(())
511 } else {
512 let tokens = e.clone().to_rust(ctx.clone(), options.clone(), symbols)?;
513 if options.clone_str_attribute_returns
519 && matches!(e.value, ExprType::Attribute(_))
520 {
521 quote!((#tokens).clone())
522 } else {
523 tokens
524 }
525 };
526 Ok(return_tokens(&ctx, value))
527 }
528 StatementType::If(i) => i.to_rust(ctx, options, symbols),
529 StatementType::For(f) => f.to_rust(ctx, options, symbols),
530 StatementType::While(w) => w.to_rust(ctx, options, symbols),
531 StatementType::Try(t) => t.to_rust(ctx, options, symbols),
532 StatementType::AsyncWith(aw) => aw.to_rust(ctx, options, symbols),
533 StatementType::AsyncFor(af) => af.to_rust(ctx, options, symbols),
534 StatementType::Raise(r) => r.to_rust(ctx, options, symbols),
535 StatementType::With(w) => w.to_rust(ctx, options, symbols),
536 _ => {
537 let error = err_from(StatementNotYetImplemented(self));
538 Err(error.into())
539 }
540 }
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547
548 #[test]
549 fn check_pass_statement() {
550 let statement = StatementType::Pass;
551 let options = PythonOptions::default();
552 let tokens = statement.clone().to_rust(
553 CodeGenContext::Module("".to_string()),
554 options,
555 SymbolTableScopes::new(),
556 );
557
558 debug!("statement: {:?}, tokens: {:?}", statement, tokens);
559 assert_eq!(tokens.unwrap().is_empty(), true);
560 }
561
562 #[test]
563 fn check_break_statement() {
564 let statement = StatementType::Break;
565 let options = PythonOptions::default();
566 let tokens = statement.clone().to_rust(
567 CodeGenContext::Module("".to_string()),
568 options,
569 SymbolTableScopes::new(),
570 );
571
572 debug!("statement: {:?}, tokens: {:?}", statement, tokens);
573 assert_eq!(tokens.unwrap().is_empty(), false);
574 }
575
576 #[test]
577 fn check_continue_statement() {
578 let statement = StatementType::Continue;
579 let options = PythonOptions::default();
580 let tokens = statement.clone().to_rust(
581 CodeGenContext::Module("".to_string()),
582 options,
583 SymbolTableScopes::new(),
584 );
585
586 debug!("statement: {:?}, tokens: {:?}", statement, tokens);
587 assert_eq!(tokens.unwrap().is_empty(), false);
588 }
589
590 #[test]
591 fn return_with_nothing() {
592 let tree = crate::parse("return", "<none>").unwrap();
593 assert_eq!(tree.raw.body.len(), 1);
594 assert_eq!(
595 tree.raw.body[0].statement,
596 StatementType::Return(Some(Expr {
597 value: crate::tree::ExprType::NoneType(crate::tree::Constant(None)),
598 lineno: Some(1),
599 col_offset: Some(0),
600 end_lineno: Some(1),
601 end_col_offset: Some(6),
602 ..Default::default()
603 }))
604 );
605 }
606
607 #[test]
608 fn return_with_expr() {
609 let lit = litrs::Literal::Integer(litrs::IntegerLit::parse(String::from("8")).unwrap());
610 let tree = crate::parse("return 8", "<none>").unwrap();
611 assert_eq!(tree.raw.body.len(), 1);
612 assert_eq!(
613 tree.raw.body[0].statement,
614 StatementType::Return(Some(Expr {
615 value: crate::tree::ExprType::Constant(crate::tree::Constant(Some(lit))),
616 lineno: Some(1),
617 col_offset: Some(0),
618 end_lineno: Some(1),
619 end_col_offset: Some(8),
620 ..Default::default()
621 }))
622 );
623 }
624
625 #[test]
626 fn does_module_compile() {
627 let options = PythonOptions::default();
628 let result = crate::parse(
629 "#test comment
630def foo():
631 continue
632 pass
633",
634 "test_case",
635 )
636 .unwrap();
637 tracing::info!("{:?}", result);
638 let code = result.to_rust(
639 CodeGenContext::Module("".to_string()),
640 options,
641 SymbolTableScopes::new(),
642 );
643 tracing::info!("module: {:?}", code);
644 }
645}