python_ast/ast/tree/
assign.rs1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{extraction_failure,
7 CodeGen, CodeGenContext, ExprType, PythonOptions, SymbolTableNode,
8 SymbolTableScopes,
9};
10
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12pub struct Assign {
13 pub targets: Vec<ExprType>,
14 pub value: ExprType,
15 pub type_comment: Option<String>,
16}
17
18impl<'a, 'py> FromPyObject<'a, 'py> for Assign {
19 type Error = pyo3::PyErr;
20 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
21 let targets: Vec<ExprType> = ob
22 .getattr("targets")
23 .map_err(|e| extraction_failure("assignment targets", &ob, e))?
24 .extract()
25 .map_err(|e| extraction_failure("assignment targets", &ob, e))?;
26
27 let python_value = ob.getattr("value").map_err(|e| extraction_failure("value", &ob, e))?;
28
29 let value = python_value.extract().map_err(|e| extraction_failure("python_value", &ob, e))?;
30
31 Ok(Assign {
32 targets: targets,
33 value: value,
34 type_comment: None,
35 })
36 }
37}
38
39impl<'a> CodeGen for Assign {
40 type Context = CodeGenContext;
41 type Options = PythonOptions;
42 type SymbolTable = SymbolTableScopes;
43
44 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
45 let mut symbols = symbols;
46 let mut position = 0;
47 for target in self.targets {
48 if let ExprType::Name(name) = target {
50 symbols.insert(
51 name.id,
52 SymbolTableNode::Assign {
53 position: position,
54 value: self.value.clone(),
55 },
56 );
57 }
58 position += 1;
60 }
61 symbols
62 }
63
64 fn to_rust(
65 self,
66 ctx: Self::Context,
67 options: Self::Options,
68 symbols: Self::SymbolTable,
69 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
70 let value_is_none_early = crate::is_none_expr(&self.value);
71 let value_yields_option = crate::expr_yields_option(&self.value, &options, &symbols);
72 let value_expr = self.value.clone();
73 let value = self
74 .value
75 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
76
77 let value_is_none = value_is_none_early;
88 let value_is_str_literal = matches!(
92 &value_expr,
93 ExprType::Constant(c) if matches!(&c.0, Some(litrs::Literal::String(_)))
94 );
95 let render_one = |target: &ExprType,
96 value: &TokenStream|
97 -> Result<TokenStream, Box<dyn std::error::Error>> {
98 let target_code =
99 target
100 .clone()
101 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
102 Ok(match target {
103 ExprType::Name(name) => {
104 if !value_is_none
105 && !value_yields_option
106 && options.optional_names.contains(&name.id)
107 {
108 quote!(#target_code = Some(#value);)
109 } else {
110 quote!(#target_code = #value;)
111 }
112 }
113 ExprType::Tuple(_) => quote!((#target_code) = #value;),
115 ExprType::Attribute(_) if value_is_str_literal => {
116 quote!(#target_code = (#value).to_string();)
117 }
118 _ => quote!(#target_code = #value;),
119 })
120 };
121
122 let render_subscript_store = |sub: &crate::Subscript,
127 value: &TokenStream|
128 -> Result<TokenStream, Box<dyn std::error::Error>> {
129 let receiver = crate::subscript_receiver_place(
133 &sub.value,
134 ctx.clone(),
135 options.clone(),
136 symbols.clone(),
137 )?;
138 match &sub.kind {
139 crate::SubscriptKind::Index(index) => {
140 let index = index
141 .clone()
142 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
143 Ok(quote!((#receiver).py_set_index(#index, #value)?;))
144 }
145 crate::SubscriptKind::Slice { .. } => Err(
146 "slice assignment (`x[a:b] = ...`) is not yet supported"
147 .to_string()
148 .into(),
149 ),
150 }
151 };
152
153 let render = |target: &ExprType,
154 value: &TokenStream|
155 -> Result<TokenStream, Box<dyn std::error::Error>> {
156 match target {
157 ExprType::Subscript(sub) => render_subscript_store(sub, value),
158 _ => render_one(target, value),
159 }
160 };
161
162 if self.targets.len() == 1 {
163 if let ExprType::Name(name) = &self.targets[0] {
168 if options.optional_names.contains(&name.id) {
169 let target_code = self.targets[0].clone().to_rust(
170 ctx.clone(),
171 options.clone(),
172 symbols.clone(),
173 )?;
174 let value = crate::lower_optional_value(
175 &value_expr,
176 ctx.clone(),
177 options.clone(),
178 symbols.clone(),
179 )?;
180 return Ok(quote!(#target_code = #value;));
181 }
182 }
183 render(&self.targets[0], &value)
184 } else {
185 let mut stream = quote!(let __rython_chain = #value;);
188 for target in &self.targets {
189 stream.extend(render(target, "e!(__rython_chain.clone()))?);
190 }
191 Ok(stream)
192 }
193 }
194}