python_ast/ast/tree/
yield_expr.rs

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, SymbolTableScopes,
8};
9
10/// Yield expression (yield value)
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12pub struct Yield {
13    /// The value being yielded (optional)
14    pub value: Option<Box<ExprType>>,
15    /// Position information
16    pub lineno: Option<usize>,
17    pub col_offset: Option<usize>,
18    pub end_lineno: Option<usize>,
19    pub end_col_offset: Option<usize>,
20}
21
22/// Yield from expression (yield from iterable)
23#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
24pub struct YieldFrom {
25    /// The iterable being yielded from
26    pub value: Box<ExprType>,
27    /// Position information
28    pub lineno: Option<usize>,
29    pub col_offset: Option<usize>,
30    pub end_lineno: Option<usize>,
31    pub end_col_offset: Option<usize>,
32}
33
34impl<'a> FromPyObject<'a> for Yield {
35    fn extract_bound(ob: &Bound<'a, 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> FromPyObject<'a> for YieldFrom {
58    fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
59        // Extract value
60        let value: ExprType = ob.getattr("value")?.extract()?;
61        
62        Ok(YieldFrom {
63            value: Box::new(value),
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 Node for Yield {
73    fn lineno(&self) -> Option<usize> { self.lineno }
74    fn col_offset(&self) -> Option<usize> { self.col_offset }
75    fn end_lineno(&self) -> Option<usize> { self.end_lineno }
76    fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
77}
78
79impl Node for YieldFrom {
80    fn lineno(&self) -> Option<usize> { self.lineno }
81    fn col_offset(&self) -> Option<usize> { self.col_offset }
82    fn end_lineno(&self) -> Option<usize> { self.end_lineno }
83    fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
84}
85
86impl CodeGen for Yield {
87    type Context = CodeGenContext;
88    type Options = PythonOptions;
89    type SymbolTable = SymbolTableScopes;
90
91    fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
92        if let Some(value) = self.value {
93            (*value).find_symbols(symbols)
94        } else {
95            symbols
96        }
97    }
98
99    fn to_rust(
100        self,
101        ctx: Self::Context,
102        options: Self::Options,
103        symbols: Self::SymbolTable,
104    ) -> Result<TokenStream, Box<dyn std::error::Error>> {
105        if let Some(value) = self.value {
106            let value_tokens = (*value).to_rust(ctx, options, symbols)?;
107            // For now, generate a simple return since Rust doesn't have yield expressions
108            // In practice, this would need to be part of an async generator or similar
109            Ok(quote! {
110                // Yield expression - simplified translation
111                // Python's yield doesn't map directly to Rust
112                #value_tokens
113            })
114        } else {
115            Ok(quote! {
116                // Bare yield - simplified translation
117                ()
118            })
119        }
120    }
121}
122
123impl CodeGen for YieldFrom {
124    type Context = CodeGenContext;
125    type Options = PythonOptions;
126    type SymbolTable = SymbolTableScopes;
127
128    fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
129        (*self.value).find_symbols(symbols)
130    }
131
132    fn to_rust(
133        self,
134        ctx: Self::Context,
135        options: Self::Options,
136        symbols: Self::SymbolTable,
137    ) -> Result<TokenStream, Box<dyn std::error::Error>> {
138        let value_tokens = (*self.value).to_rust(ctx, options, symbols)?;
139        // For now, generate a simple expression since Rust doesn't have yield from
140        Ok(quote! {
141            // Yield from expression - simplified translation
142            #value_tokens
143        })
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    // Tests would go here - currently commented out as they need full AST infrastructure
150    // create_parse_test!(test_simple_yield, "def gen(): yield 42", "test.py");
151}