Skip to main content

python_ast/ast/tree/
yield_expr.rs

1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    CodeGen, CodeGenContext, ExprType, Node, PythonOptions, SymbolTableScopes,
7};
8
9/// Yield expression (yield value)
10#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
11pub struct Yield {
12    /// The value being yielded (optional)
13    pub value: Option<Box<ExprType>>,
14    /// Position information
15    pub lineno: Option<usize>,
16    pub col_offset: Option<usize>,
17    pub end_lineno: Option<usize>,
18    pub end_col_offset: Option<usize>,
19}
20
21/// Yield from expression (yield from iterable)
22#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
23pub struct YieldFrom {
24    /// The iterable being yielded from
25    pub value: Box<ExprType>,
26    /// Position information
27    pub lineno: Option<usize>,
28    pub col_offset: Option<usize>,
29    pub end_lineno: Option<usize>,
30    pub end_col_offset: Option<usize>,
31}
32
33impl<'a, 'py> FromPyObject<'a, 'py> for Yield {
34    type Error = pyo3::PyErr;
35    fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
36        // Extract value (optional)
37        let value: Option<Box<ExprType>> = if let Ok(value_attr) = ob.getattr("value") {
38            if value_attr.is_none() {
39                None
40            } else {
41                Some(Box::new(value_attr.extract()?))
42            }
43        } else {
44            None
45        };
46        
47        Ok(Yield {
48            value,
49            lineno: ob.lineno(),
50            col_offset: ob.col_offset(),
51            end_lineno: ob.end_lineno(),
52            end_col_offset: ob.end_col_offset(),
53        })
54    }
55}
56
57impl<'a, 'py> FromPyObject<'a, 'py> for YieldFrom {
58    type Error = pyo3::PyErr;
59    fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
60        // Extract value
61        let value: ExprType = ob.getattr("value")?.extract()?;
62        
63        Ok(YieldFrom {
64            value: Box::new(value),
65            lineno: ob.lineno(),
66            col_offset: ob.col_offset(),
67            end_lineno: ob.end_lineno(),
68            end_col_offset: ob.end_col_offset(),
69        })
70    }
71}
72
73impl Node for Yield {
74    fn lineno(&self) -> Option<usize> { self.lineno }
75    fn col_offset(&self) -> Option<usize> { self.col_offset }
76    fn end_lineno(&self) -> Option<usize> { self.end_lineno }
77    fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
78}
79
80impl Node for YieldFrom {
81    fn lineno(&self) -> Option<usize> { self.lineno }
82    fn col_offset(&self) -> Option<usize> { self.col_offset }
83    fn end_lineno(&self) -> Option<usize> { self.end_lineno }
84    fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
85}
86
87impl CodeGen for Yield {
88    type Context = CodeGenContext;
89    type Options = PythonOptions;
90    type SymbolTable = SymbolTableScopes;
91
92    fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
93        if let Some(value) = self.value {
94            (*value).find_symbols(symbols)
95        } else {
96            symbols
97        }
98    }
99
100    fn to_rust(
101        self,
102        _ctx: Self::Context,
103        _options: Self::Options,
104        _symbols: Self::SymbolTable,
105    ) -> Result<TokenStream, Box<dyn std::error::Error>> {
106        // A generator silently becoming a plain function is exactly the kind
107        // of divergence that must fail loudly instead.
108        Err(
109            "generators (`yield`) are not supported yet: the function would \
110             silently evaluate a single value instead of producing a \
111             generator. Rewrite it to build and return a list."
112                .to_string()
113                .into(),
114        )
115    }
116}
117
118impl CodeGen for YieldFrom {
119    type Context = CodeGenContext;
120    type Options = PythonOptions;
121    type SymbolTable = SymbolTableScopes;
122
123    fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
124        (*self.value).find_symbols(symbols)
125    }
126
127    fn to_rust(
128        self,
129        _ctx: Self::Context,
130        _options: Self::Options,
131        _symbols: Self::SymbolTable,
132    ) -> Result<TokenStream, Box<dyn std::error::Error>> {
133        Err(
134            "generators (`yield from`) are not supported yet: the function \
135             would silently evaluate the iterable once instead of delegating \
136             to it. Rewrite it to build and return a list."
137                .to_string()
138                .into(),
139        )
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    // Tests would go here - currently commented out as they need full AST infrastructure
146    // create_parse_test!(test_simple_yield, "def gen(): yield 42", "test.py");
147}