Skip to main content

oxilean_codegen/python_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::*;
9use super::functions::{FromImports, PYTHON_KEYWORDS};
10
11/// A class variable (field) in a Python class.
12#[derive(Debug, Clone, PartialEq)]
13pub struct PythonClassVar {
14    /// Field name
15    pub name: String,
16    /// Type annotation
17    pub annotation: PythonType,
18    /// Optional default value
19    pub default: Option<PythonExpr>,
20}
21/// A Python function definition.
22#[derive(Debug, Clone, PartialEq)]
23pub struct PythonFunction {
24    /// Function name
25    pub name: String,
26    /// Parameters
27    pub params: Vec<PythonParam>,
28    /// Return type annotation
29    pub return_type: Option<PythonType>,
30    /// Function body
31    pub body: Vec<PythonStmt>,
32    /// Decorator expressions (e.g. `property`, `classmethod`, `staticmethod`)
33    pub decorators: Vec<String>,
34    /// Whether this is an async function
35    pub is_async: bool,
36    /// Whether this is a classmethod
37    pub is_classmethod: bool,
38    /// Whether this is a staticmethod
39    pub is_staticmethod: bool,
40}
41impl PythonFunction {
42    /// Create a new function with just a name, empty body.
43    pub fn new(name: impl Into<String>) -> Self {
44        PythonFunction {
45            name: name.into(),
46            params: Vec::new(),
47            return_type: None,
48            body: Vec::new(),
49            decorators: Vec::new(),
50            is_async: false,
51            is_classmethod: false,
52            is_staticmethod: false,
53        }
54    }
55}
56/// A Python class definition.
57#[derive(Debug, Clone, PartialEq)]
58pub struct PythonClass {
59    /// Class name
60    pub name: String,
61    /// Base classes
62    pub bases: Vec<String>,
63    /// Methods
64    pub methods: Vec<PythonFunction>,
65    /// Class variables / fields (for dataclasses)
66    pub class_vars: Vec<PythonClassVar>,
67    /// Whether to annotate with `@dataclass`
68    pub is_dataclass: bool,
69    /// Whether to annotate with `ABC` base or `@abstractmethod`
70    pub is_abstract: bool,
71    /// Additional decorators
72    pub decorators: Vec<String>,
73    /// Docstring
74    pub docstring: Option<String>,
75}
76impl PythonClass {
77    /// Create a new empty class.
78    pub fn new(name: impl Into<String>) -> Self {
79        PythonClass {
80            name: name.into(),
81            bases: Vec::new(),
82            methods: Vec::new(),
83            class_vars: Vec::new(),
84            is_dataclass: false,
85            is_abstract: false,
86            decorators: Vec::new(),
87            docstring: None,
88        }
89    }
90}
91/// Python function parameter with optional type annotation and default.
92#[derive(Debug, Clone, PartialEq)]
93pub struct PythonParam {
94    /// Parameter name
95    pub name: String,
96    /// Optional type annotation
97    pub annotation: Option<PythonType>,
98    /// Optional default value
99    pub default: Option<PythonExpr>,
100    /// Whether this is a `*args` parameter
101    pub is_vararg: bool,
102    /// Whether this is a `**kwargs` parameter
103    pub is_kwarg: bool,
104    /// Whether this is a keyword-only parameter (after `*`)
105    pub is_keyword_only: bool,
106}
107impl PythonParam {
108    /// Create a simple parameter with just a name.
109    pub fn simple(name: impl Into<String>) -> Self {
110        PythonParam {
111            name: name.into(),
112            annotation: None,
113            default: None,
114            is_vararg: false,
115            is_kwarg: false,
116            is_keyword_only: false,
117        }
118    }
119    /// Create a parameter with a type annotation.
120    pub fn typed(name: impl Into<String>, ty: PythonType) -> Self {
121        PythonParam {
122            name: name.into(),
123            annotation: Some(ty),
124            default: None,
125            is_vararg: false,
126            is_kwarg: false,
127            is_keyword_only: false,
128        }
129    }
130}
131/// A match case arm: `case <pattern>: <body>`
132#[derive(Debug, Clone, PartialEq)]
133pub struct MatchArm {
134    /// The pattern (as raw Python text for flexibility)
135    pub pattern: String,
136    /// Optional guard: `if guard`
137    pub guard: Option<PythonExpr>,
138    /// Body statements
139    pub body: Vec<PythonStmt>,
140}
141/// Python literal value.
142#[derive(Debug, Clone, PartialEq)]
143pub enum PythonLit {
144    /// Integer literal: `42`, `-7`
145    Int(i64),
146    /// Float literal: `3.14`, `-0.5`
147    Float(f64),
148    /// String literal: `"hello"` or `'hello'`
149    Str(String),
150    /// Boolean literal: `True` or `False`
151    Bool(bool),
152    /// `None` literal
153    None,
154    /// Bytes literal: `b"data"`
155    Bytes(Vec<u8>),
156    /// Ellipsis: `...`
157    Ellipsis,
158}
159/// Python code generation backend.
160///
161/// Compiles LCNF function declarations to a `PythonModule` containing
162/// Python 3.10+ code with type hints.
163pub struct PythonBackend {
164    /// The module being built.
165    pub module: PythonModule,
166    /// Mapping from LCNF names to mangled Python names.
167    pub fn_map: HashMap<String, String>,
168    /// Counter for generating fresh temporary variable names.
169    pub fresh_counter: usize,
170}
171impl PythonBackend {
172    /// Create a new Python backend.
173    pub fn new() -> Self {
174        PythonBackend {
175            module: PythonModule::new(),
176            fn_map: HashMap::new(),
177            fresh_counter: 0,
178        }
179    }
180    /// Generate a fresh temporary variable name: `_t0`, `_t1`, etc.
181    pub fn fresh_var(&mut self) -> String {
182        let n = self.fresh_counter;
183        self.fresh_counter += 1;
184        format!("_t{}", n)
185    }
186    /// Mangle an LCNF name into a valid Python identifier.
187    ///
188    /// Rules:
189    /// - Replace `.` with `_`
190    /// - Replace `'` (prime) with `_prime`
191    /// - Replace `-` with `_`
192    /// - Prefix Python reserved words with `_`
193    pub fn mangle_name(&self, name: &str) -> String {
194        let mangled: String = name
195            .chars()
196            .map(|c| match c {
197                '.' | '\'' | '-' | ' ' => '_',
198                c if c.is_alphanumeric() || c == '_' => c,
199                _ => '_',
200            })
201            .collect();
202        if PYTHON_KEYWORDS.contains(&mangled.as_str())
203            || mangled.starts_with(|c: char| c.is_ascii_digit())
204        {
205            format!("_{}", mangled)
206        } else if mangled.is_empty() {
207            "_anon".to_string()
208        } else {
209            mangled
210        }
211    }
212    /// Top-level entry point: compile a slice of LCNF function declarations
213    /// into a Python module string.
214    pub fn compile_module(decls: &[LcnfFunDecl]) -> Result<String, String> {
215        let mut backend = PythonBackend::new();
216        backend.module.add_from_import(
217            "typing",
218            vec![
219                ("Any".to_string(), None),
220                ("Optional".to_string(), None),
221                ("Union".to_string(), None),
222                ("List".to_string(), None),
223                ("Dict".to_string(), None),
224                ("Tuple".to_string(), None),
225                ("Callable".to_string(), None),
226            ],
227        );
228        for decl in decls {
229            let py_name = backend.mangle_name(&decl.name);
230            backend.fn_map.insert(decl.name.clone(), py_name);
231        }
232        for decl in decls {
233            let func = backend.compile_decl(decl)?;
234            backend.module.add_function(func);
235        }
236        for decl in decls {
237            if let Some(py_name) = backend.fn_map.get(&decl.name) {
238                backend.module.all_exports.push(py_name.clone());
239            }
240        }
241        Ok(backend.module.emit())
242    }
243    /// Compile a single LCNF function declaration into a `PythonFunction`.
244    pub fn compile_decl(&mut self, decl: &LcnfFunDecl) -> Result<PythonFunction, String> {
245        let py_name = self.mangle_name(&decl.name);
246        let mut func = PythonFunction::new(py_name);
247        for param in &decl.params {
248            let param_name = format!("_v{}", param.id.0);
249            func.params.push(PythonParam {
250                name: param_name,
251                annotation: Some(PythonType::Any),
252                default: None,
253                is_vararg: false,
254                is_kwarg: false,
255                is_keyword_only: false,
256            });
257        }
258        func.return_type = Some(PythonType::Any);
259        let body_stmts = self.compile_expr_to_stmts(&decl.body)?;
260        func.body = body_stmts;
261        Ok(func)
262    }
263    /// Compile an LCNF expression into a list of Python statements,
264    /// ending with a `return`.
265    pub(super) fn compile_expr_to_stmts(
266        &mut self,
267        expr: &LcnfExpr,
268    ) -> Result<Vec<PythonStmt>, String> {
269        self.compile_expr_stmts_inner(expr, &mut Vec::new())
270    }
271    /// Compile an LCNF expression into a sequence of Python statements.
272    pub(super) fn compile_expr_stmts_inner(
273        &mut self,
274        expr: &LcnfExpr,
275        stmts: &mut Vec<PythonStmt>,
276    ) -> Result<Vec<PythonStmt>, String> {
277        match expr {
278            LcnfExpr::Let {
279                name, value, body, ..
280            } => {
281                let py_name = self.mangle_name(name);
282                let py_val = self.compile_let_value(value)?;
283                stmts.push(PythonStmt::Assign(vec![PythonExpr::Var(py_name)], py_val));
284                self.compile_expr_stmts_inner(body, stmts)
285            }
286            LcnfExpr::Return(arg) => {
287                let py_expr = self.compile_arg(arg);
288                stmts.push(PythonStmt::Return(Some(py_expr)));
289                Ok(stmts.clone())
290            }
291            LcnfExpr::Unreachable => {
292                stmts.push(PythonStmt::Raise(Some(PythonExpr::Call(
293                    Box::new(PythonExpr::Var("RuntimeError".to_string())),
294                    vec![PythonExpr::Lit(PythonLit::Str("unreachable".to_string()))],
295                    vec![],
296                ))));
297                Ok(stmts.clone())
298            }
299            LcnfExpr::TailCall(func, args) => {
300                let py_func = self.compile_arg(func);
301                let py_args: Vec<PythonExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
302                stmts.push(PythonStmt::Return(Some(PythonExpr::Call(
303                    Box::new(py_func),
304                    py_args,
305                    vec![],
306                ))));
307                Ok(stmts.clone())
308            }
309            LcnfExpr::Case {
310                scrutinee,
311                alts,
312                default,
313                ..
314            } => {
315                let scrutinee_name = format!("_v{}", scrutinee.0);
316                let match_arms: Vec<MatchArm> = alts
317                    .iter()
318                    .map(|alt| {
319                        let pattern = if alt.params.is_empty() {
320                            alt.ctor_name.clone()
321                        } else {
322                            let param_names: Vec<String> =
323                                alt.params.iter().map(|p| format!("_v{}", p.id.0)).collect();
324                            format!("{}({})", alt.ctor_name, param_names.join(", "))
325                        };
326                        let mut arm_stmts = Vec::new();
327                        let _ = self.compile_expr_stmts_inner(&alt.body, &mut arm_stmts);
328                        MatchArm {
329                            pattern,
330                            guard: None,
331                            body: arm_stmts,
332                        }
333                    })
334                    .collect();
335                let mut all_arms = match_arms;
336                if let Some(def) = default {
337                    let mut def_stmts = Vec::new();
338                    let _ = self.compile_expr_stmts_inner(def, &mut def_stmts);
339                    all_arms.push(MatchArm {
340                        pattern: "_".to_string(),
341                        guard: None,
342                        body: def_stmts,
343                    });
344                }
345                stmts.push(PythonStmt::Match(PythonExpr::Var(scrutinee_name), all_arms));
346                Ok(stmts.clone())
347            }
348        }
349    }
350    /// Compile an LCNF argument (atomic value) into a Python expression.
351    pub(super) fn compile_arg(&self, arg: &LcnfArg) -> PythonExpr {
352        match arg {
353            LcnfArg::Var(id) => PythonExpr::Var(format!("_v{}", id.0)),
354            LcnfArg::Lit(lit) => self.compile_lit(lit),
355            LcnfArg::Erased => PythonExpr::Lit(PythonLit::None),
356            LcnfArg::Type(_) => PythonExpr::Lit(PythonLit::None),
357        }
358    }
359    /// Compile an LCNF let-bound value into a Python expression.
360    pub(super) fn compile_let_value(&mut self, value: &LcnfLetValue) -> Result<PythonExpr, String> {
361        match value {
362            LcnfLetValue::App(func, args) => {
363                let py_func = self.compile_arg(func);
364                let py_args: Vec<PythonExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
365                Ok(PythonExpr::Call(Box::new(py_func), py_args, vec![]))
366            }
367            LcnfLetValue::Proj(_, idx, var) => {
368                let obj = PythonExpr::Var(format!("_v{}", var.0));
369                Ok(PythonExpr::Subscript(
370                    Box::new(obj),
371                    Box::new(PythonExpr::Lit(PythonLit::Int(*idx as i64))),
372                ))
373            }
374            LcnfLetValue::Ctor(name, _, args) => {
375                let py_args: Vec<PythonExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
376                Ok(PythonExpr::Call(
377                    Box::new(PythonExpr::Var(self.mangle_name(name))),
378                    py_args,
379                    vec![],
380                ))
381            }
382            LcnfLetValue::Lit(lit) => Ok(self.compile_lit(lit)),
383            LcnfLetValue::Erased => Ok(PythonExpr::Lit(PythonLit::None)),
384            LcnfLetValue::FVar(id) => Ok(PythonExpr::Var(format!("_v{}", id.0))),
385            LcnfLetValue::Reset(id) => Ok(PythonExpr::Var(format!("_v{}", id.0))),
386            LcnfLetValue::Reuse(id, name, _, args) => {
387                let py_args: Vec<PythonExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
388                let _ = id;
389                Ok(PythonExpr::Call(
390                    Box::new(PythonExpr::Var(self.mangle_name(name))),
391                    py_args,
392                    vec![],
393                ))
394            }
395        }
396    }
397    /// Compile an LCNF literal into a Python expression.
398    pub(super) fn compile_lit(&self, lit: &LcnfLit) -> PythonExpr {
399        match lit {
400            LcnfLit::Nat(n) => PythonExpr::Lit(PythonLit::Int(*n as i64)),
401            LcnfLit::Int(i) => PythonExpr::Lit(PythonLit::Int(*i)),
402            LcnfLit::Str(s) => PythonExpr::Lit(PythonLit::Str(s.clone())),
403        }
404    }
405    /// Emit the compiled module as a Python string.
406    pub fn emit_module(&self) -> String {
407        self.module.emit()
408    }
409}
410/// Python type annotation for type-hinted code generation (PEP 484).
411#[derive(Debug, Clone, PartialEq, Eq, Hash)]
412pub enum PythonType {
413    /// `int`
414    Int,
415    /// `float`
416    Float,
417    /// `str`
418    Str,
419    /// `bool`
420    Bool,
421    /// `None`
422    None_,
423    /// `list[T]`
424    List(Box<PythonType>),
425    /// `dict[K, V]`
426    Dict(Box<PythonType>, Box<PythonType>),
427    /// `tuple[T1, T2, ...]`
428    Tuple(Vec<PythonType>),
429    /// `T | None` (Optional\[T\])
430    Optional(Box<PythonType>),
431    /// `T1 | T2 | ...` (Union)
432    Union(Vec<PythonType>),
433    /// User-defined type / class name
434    Custom(String),
435    /// `Any` (typing.Any)
436    Any,
437    /// `Callable[[A, B], R]`
438    Callable,
439    /// `set[T]`
440    Set(Box<PythonType>),
441    /// `frozenset[T]`
442    FrozenSet(Box<PythonType>),
443    /// `Generator[Y, S, R]`
444    Generator(Box<PythonType>, Box<PythonType>, Box<PythonType>),
445    /// `AsyncGenerator[Y, S]`
446    AsyncGenerator(Box<PythonType>, Box<PythonType>),
447    /// `Iterator[T]`
448    Iterator(Box<PythonType>),
449    /// `Iterable[T]`
450    Iterable(Box<PythonType>),
451    /// `Sequence[T]`
452    Sequence(Box<PythonType>),
453    /// `Mapping[K, V]`
454    Mapping(Box<PythonType>, Box<PythonType>),
455    /// `ClassVar[T]`
456    ClassVar(Box<PythonType>),
457    /// `Final[T]`
458    Final(Box<PythonType>),
459    /// `type[T]` (the class itself)
460    Type(Box<PythonType>),
461}
462/// Part of an f-string.
463#[derive(Debug, Clone, PartialEq)]
464pub enum FStringPart {
465    /// Raw string text
466    Literal(String),
467    /// Interpolated expression: `{expr}`
468    Expr(PythonExpr),
469    /// Interpolated expression with format spec: `{expr:.2f}`
470    ExprWithFormat(PythonExpr, String),
471}
472/// Python statement for code generation.
473#[derive(Debug, Clone, PartialEq)]
474pub enum PythonStmt {
475    /// Expression statement: `expr`
476    Expr(PythonExpr),
477    /// Simple assignment: `target = value`
478    Assign(Vec<PythonExpr>, PythonExpr),
479    /// Augmented assignment: `target op= value`
480    AugAssign(PythonExpr, String, PythonExpr),
481    /// Annotated assignment: `target: type = value`
482    AnnAssign(String, PythonType, Option<PythonExpr>),
483    /// If/elif/else statement
484    If(
485        PythonExpr,
486        Vec<PythonStmt>,
487        Vec<(PythonExpr, Vec<PythonStmt>)>,
488        Vec<PythonStmt>,
489    ),
490    /// For loop: `for var in iter: body`
491    For(String, PythonExpr, Vec<PythonStmt>, Vec<PythonStmt>),
492    /// While loop: `while cond: body`
493    While(PythonExpr, Vec<PythonStmt>, Vec<PythonStmt>),
494    /// With statement: `with expr as var: body`
495    With(Vec<(PythonExpr, Option<String>)>, Vec<PythonStmt>),
496    /// Try/except/else/finally statement
497    Try(
498        Vec<PythonStmt>,
499        Vec<(Option<PythonExpr>, Option<String>, Vec<PythonStmt>)>,
500        Vec<PythonStmt>,
501        Vec<PythonStmt>,
502    ),
503    /// Return statement: `return expr`
504    Return(Option<PythonExpr>),
505    /// Raise statement: `raise expr`
506    Raise(Option<PythonExpr>),
507    /// Delete statement: `del name`
508    Del(Vec<PythonExpr>),
509    /// Pass statement: `pass`
510    Pass,
511    /// Break statement: `break`
512    Break,
513    /// Continue statement: `continue`
514    Continue,
515    /// Import statement: `import module` or `import module as alias`
516    Import(Vec<(String, Option<String>)>),
517    /// From-import statement: `from module import name` or `from module import *`
518    From(String, Vec<(String, Option<String>)>),
519    /// Class definition
520    ClassDef(PythonClass),
521    /// Function definition
522    FuncDef(PythonFunction),
523    /// Async function definition
524    AsyncFuncDef(PythonFunction),
525    /// Docstring statement
526    Docstring(String),
527    /// Assert statement: `assert expr, msg`
528    Assert(PythonExpr, Option<PythonExpr>),
529    /// Global declaration: `global x, y`
530    Global(Vec<String>),
531    /// Nonlocal declaration: `nonlocal x, y`
532    Nonlocal(Vec<String>),
533    /// Match statement (Python 3.10+)
534    Match(PythonExpr, Vec<MatchArm>),
535    /// Raw Python text (escape hatch)
536    Raw(String),
537}
538/// A complete Python module (one `.py` file).
539#[derive(Debug, Clone)]
540pub struct PythonModule {
541    /// Module-level imports: `import X` or `import X as Y`
542    pub imports: Vec<(String, Option<String>)>,
543    /// From-imports: `from X import Y` or `from X import Y as Z`
544    pub from_imports: FromImports,
545    /// Top-level class definitions
546    pub classes: Vec<PythonClass>,
547    /// Top-level function definitions
548    pub functions: Vec<PythonFunction>,
549    /// Other top-level statements
550    pub statements: Vec<PythonStmt>,
551    /// Module docstring
552    pub module_docstring: Option<String>,
553    /// `__all__` exports
554    pub all_exports: Vec<String>,
555}
556impl PythonModule {
557    /// Create a new empty Python module.
558    pub fn new() -> Self {
559        PythonModule {
560            imports: Vec::new(),
561            from_imports: Vec::new(),
562            classes: Vec::new(),
563            functions: Vec::new(),
564            statements: Vec::new(),
565            module_docstring: None,
566            all_exports: Vec::new(),
567        }
568    }
569    /// Add a module-level import.
570    pub fn add_import(&mut self, module: impl Into<String>, alias: Option<String>) {
571        self.imports.push((module.into(), alias));
572    }
573    /// Add a from-import.
574    pub fn add_from_import(
575        &mut self,
576        module: impl Into<String>,
577        names: Vec<(String, Option<String>)>,
578    ) {
579        self.from_imports.push((module.into(), names));
580    }
581    /// Add a class definition.
582    pub fn add_class(&mut self, cls: PythonClass) {
583        self.classes.push(cls);
584    }
585    /// Add a function definition.
586    pub fn add_function(&mut self, func: PythonFunction) {
587        self.functions.push(func);
588    }
589    /// Add a top-level statement.
590    pub fn add_statement(&mut self, stmt: PythonStmt) {
591        self.statements.push(stmt);
592    }
593    /// Emit the full Python module as a string.
594    pub fn emit(&self) -> String {
595        let mut out = String::new();
596        if let Some(doc) = &self.module_docstring {
597            out.push_str(&format!("\"\"\"{}\"\"\"\n\n", doc));
598        }
599        let future_imports: Vec<_> = self
600            .from_imports
601            .iter()
602            .filter(|(m, _)| m == "__future__")
603            .collect();
604        for (module, names) in &future_imports {
605            out.push_str(&format_from_import(module, names));
606            out.push('\n');
607        }
608        if !future_imports.is_empty() {
609            out.push('\n');
610        }
611        for (module, alias) in &self.imports {
612            match alias {
613                Some(a) => out.push_str(&format!("import {} as {}\n", module, a)),
614                None => out.push_str(&format!("import {}\n", module)),
615            }
616        }
617        if !self.imports.is_empty() {
618            out.push('\n');
619        }
620        let non_future: Vec<_> = self
621            .from_imports
622            .iter()
623            .filter(|(m, _)| m != "__future__")
624            .collect();
625        for (module, names) in &non_future {
626            out.push_str(&format_from_import(module, names));
627            out.push('\n');
628        }
629        if !non_future.is_empty() {
630            out.push('\n');
631        }
632        if !self.all_exports.is_empty() {
633            out.push_str("__all__ = [");
634            for (i, name) in self.all_exports.iter().enumerate() {
635                if i > 0 {
636                    out.push_str(", ");
637                }
638                out.push_str(&format!("\"{}\"", name));
639            }
640            out.push_str("]\n\n");
641        }
642        for cls in &self.classes {
643            out.push_str(&emit_class(cls, 0));
644            out.push_str("\n\n");
645        }
646        for func in &self.functions {
647            out.push_str(&emit_function(func, 0));
648            out.push_str("\n\n");
649        }
650        for stmt in &self.statements {
651            out.push_str(&emit_stmt(stmt, 0));
652            out.push('\n');
653        }
654        out
655    }
656}
657/// Python expression for code generation.
658#[derive(Debug, Clone, PartialEq)]
659pub enum PythonExpr {
660    /// A literal value: `42`, `"hello"`, `True`, `None`, etc.
661    Lit(PythonLit),
662    /// A variable identifier: `x`, `my_var`
663    Var(String),
664    /// Binary operator: `lhs + rhs`, `a == b`, etc.
665    BinOp(String, Box<PythonExpr>, Box<PythonExpr>),
666    /// Unary operator: `-x`, `not x`, `~x`
667    UnaryOp(String, Box<PythonExpr>),
668    /// Function call: `f(a, b, key=val)`
669    Call(Box<PythonExpr>, Vec<PythonExpr>, Vec<(String, PythonExpr)>),
670    /// Attribute access: `obj.field`
671    Attr(Box<PythonExpr>, String),
672    /// Subscript: `obj[idx]`
673    Subscript(Box<PythonExpr>, Box<PythonExpr>),
674    /// Lambda expression: `lambda x, y: x + y`
675    Lambda(Vec<String>, Box<PythonExpr>),
676    /// Conditional (ternary) expression: `a if cond else b`
677    IfExpr(Box<PythonExpr>, Box<PythonExpr>, Box<PythonExpr>),
678    /// List comprehension: `[expr for var in iter if cond]`
679    ListComp(
680        Box<PythonExpr>,
681        String,
682        Box<PythonExpr>,
683        Option<Box<PythonExpr>>,
684    ),
685    /// Dict comprehension: `{k: v for k, v in items}`
686    DictComp(
687        Box<PythonExpr>,
688        Box<PythonExpr>,
689        String,
690        String,
691        Box<PythonExpr>,
692    ),
693    /// Set comprehension: `{x for x in iter}`
694    SetComp(
695        Box<PythonExpr>,
696        String,
697        Box<PythonExpr>,
698        Option<Box<PythonExpr>>,
699    ),
700    /// Generator expression: `(expr for var in iter if cond)`
701    GenExpr(
702        Box<PythonExpr>,
703        String,
704        Box<PythonExpr>,
705        Option<Box<PythonExpr>>,
706    ),
707    /// Tuple literal: `(a, b, c)` or `a, b, c`
708    Tuple(Vec<PythonExpr>),
709    /// List literal: `[a, b, c]`
710    List(Vec<PythonExpr>),
711    /// Dict literal: `{"key": val, ...}`
712    Dict(Vec<(PythonExpr, PythonExpr)>),
713    /// Set literal: `{a, b, c}`
714    Set(Vec<PythonExpr>),
715    /// Await expression: `await expr`
716    Await(Box<PythonExpr>),
717    /// Yield expression: `yield expr`
718    Yield(Option<Box<PythonExpr>>),
719    /// Yield from expression: `yield from expr`
720    YieldFrom(Box<PythonExpr>),
721    /// Match expression (Python 3.10+): used as target in match stmt
722    Match(Box<PythonExpr>),
723    /// f-string: `f"hello {name}!"`
724    FString(Vec<FStringPart>),
725    /// Walrus operator (assignment expression): `name := expr`
726    Walrus(String, Box<PythonExpr>),
727    /// Star expression: `*args`
728    Star(Box<PythonExpr>),
729    /// Double-star expression: `**kwargs`
730    DoubleStar(Box<PythonExpr>),
731    /// Slice: `a:b:c`
732    Slice(
733        Option<Box<PythonExpr>>,
734        Option<Box<PythonExpr>>,
735        Option<Box<PythonExpr>>,
736    ),
737}