python_ast/ast/tree/
for_stmt.rs1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, FromPyObject, PyAny, PyResult, types::PyAnyMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 CodeGen, CodeGenContext, ExprType, PythonOptions, SymbolTableScopes,
8 Node, impl_node_with_positions, PyAttributeExtractor, extract_list
9};
10
11use super::Statement;
12
13#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
14pub struct For {
15 pub target: ExprType,
16 pub iter: ExprType,
17 pub body: Vec<Statement>,
18 pub orelse: Vec<Statement>,
19 pub lineno: Option<usize>,
20 pub col_offset: Option<usize>,
21 pub end_lineno: Option<usize>,
22 pub end_col_offset: Option<usize>,
23}
24
25impl<'a, 'py> FromPyObject<'a, 'py> for For {
26 type Error = pyo3::PyErr;
27 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
28 let target = ob.extract_attr_with_context("target", "for loop target")?;
29 let iter = ob.extract_attr_with_context("iter", "for loop iterator")?;
30
31 let target = target
32 .extract()
33 .map_err(|e| crate::extraction_failure("for loop target", &ob, e))?;
34 let iter = iter
35 .extract()
36 .map_err(|e| crate::extraction_failure("for loop iterator", &ob, e))?;
37
38 let body: Vec<Statement> = extract_list(&ob, "body", "for body statements")?;
39 let orelse: Vec<Statement> = extract_list(&ob, "orelse", "for else statements")?;
40
41 Ok(For {
42 target,
43 iter,
44 body,
45 orelse,
46 lineno: ob.lineno(),
47 col_offset: ob.col_offset(),
48 end_lineno: ob.end_lineno(),
49 end_col_offset: ob.end_col_offset(),
50 })
51 }
52}
53
54impl_node_with_positions!(For { lineno, col_offset, end_lineno, end_col_offset });
55
56impl CodeGen for For {
57 type Context = CodeGenContext;
58 type Options = PythonOptions;
59 type SymbolTable = SymbolTableScopes;
60
61 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
62 let symbols = self.target.find_symbols(symbols);
63 let symbols = self.iter.find_symbols(symbols);
64 let symbols = self.body.into_iter().fold(symbols, |acc, stmt| stmt.find_symbols(acc));
65 self.orelse.into_iter().fold(symbols, |acc, stmt| stmt.find_symbols(acc))
66 }
67
68 fn to_rust(
69 self,
70 ctx: Self::Context,
71 options: Self::Options,
72 symbols: Self::SymbolTable,
73 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
74 let target = self.target.to_rust(ctx.clone(), options.clone(), symbols.clone())?;
75 let iter = self.iter.to_rust(ctx.clone(), options.clone(), symbols.clone())?;
76
77 let has_else = !self.orelse.is_empty();
78 let tracks_break = has_else && crate::loop_body_has_direct_break(&self.body);
82 let body_ctx = crate::CodeGenContext::Loop {
85 has_else: tracks_break,
86 parent: Box::new(ctx.clone()),
87 };
88 let body_stmts: Result<Vec<_>, _> = self.body
89 .into_iter()
90 .map(|stmt| stmt.to_rust(body_ctx.clone(), options.clone(), symbols.clone()))
91 .collect();
92 let body_stmts = body_stmts?;
93
94 if !has_else {
95 Ok(quote! {
96 for #target in #iter {
97 #(#body_stmts;)*
98 }
99 })
100 } else {
101 let else_stmts: Result<Vec<_>, _> = self.orelse
104 .into_iter()
105 .map(|stmt| stmt.to_rust(ctx.clone(), options.clone(), symbols.clone()))
106 .collect();
107 let else_stmts = else_stmts?;
108
109 if tracks_break {
110 Ok(quote! {
111 {
112 let mut __rython_broke = false;
113 for #target in #iter {
114 #(#body_stmts;)*
115 }
116 if !__rython_broke {
117 #(#else_stmts;)*
118 }
119 }
120 })
121 } else {
122 Ok(quote! {
124 {
125 for #target in #iter {
126 #(#body_stmts;)*
127 }
128 #(#else_stmts;)*
129 }
130 })
131 }
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::create_parse_test;
140
141 create_parse_test!(test_simple_for, "for x in range(10):\n print(x)", "for_test.py");
142 create_parse_test!(test_for_else, "for x in range(10):\n print(x)\nelse:\n print('done')", "for_test.py");
143 create_parse_test!(test_for_list, "for item in [1, 2, 3]:\n print(item)", "for_test.py");
144}