python_ast/codegen/mod.rs
1//! Code generation for Python ASTs.
2
3use std::fmt::Debug;
4
5pub mod python_options;
6pub use python_options::*;
7
8/// Reexport the CodeGen from to_tokenstream
9pub use to_tokenstream::CodeGen;
10
11/// A type to track the context of code generation.
12#[derive(Clone, Debug)]
13pub enum CodeGenContext {
14 Module(String),
15 /// Inside a class body; the payload is the class name, so method
16 /// lowering can resolve `self` (receiver typing, `self.method()` calls)
17 /// against the class's definition in the symbol table.
18 Class(String),
19 Function,
20 Async(Box<CodeGenContext>),
21 /// Directly inside a loop body. `has_else` is true when the loop carries
22 /// a Python `else` clause, in which case `break` statements must also set
23 /// the `__rython_broke` flag the loop lowering declares.
24 Loop {
25 has_else: bool,
26 parent: Box<CodeGenContext>,
27 },
28 /// Inside a `try` block body, which lowers to a closure returning
29 /// `Result<(), PyException>`; `raise` (and failed `assert`) here lower
30 /// to `return Err(...)` so the except handlers can catch them.
31 TryBlock { parent: Box<CodeGenContext> },
32 /// Inside an `except` handler body, where the caught exception is in
33 /// scope as `__rython_exc`. `parent` is the context the try statement
34 /// itself appears in (handler code runs outside the try's closure).
35 ExceptHandler { parent: Box<CodeGenContext> },
36}
37
38impl CodeGenContext {
39 /// Whether code generated here runs inside a try-block closure, so a
40 /// raised exception can `return Err(...)` to be caught by its handlers.
41 pub fn in_try_block(&self) -> bool {
42 match self {
43 CodeGenContext::TryBlock { .. } => true,
44 CodeGenContext::ExceptHandler { parent }
45 | CodeGenContext::Loop { parent, .. } => parent.in_try_block(),
46 CodeGenContext::Async(inner) => inner.in_try_block(),
47 _ => false,
48 }
49 }
50
51 /// Whether a `break`/`continue` generated here would have to cross a
52 /// try-block closure boundary to reach its loop. Walking outward, the
53 /// first `Loop` means the statement binds to a real Rust loop; hitting
54 /// `TryBlock` first means the loop is outside the closure, so the
55 /// statement must be threaded out as a `PyFlow` signal instead.
56 pub fn break_crosses_try_closure(&self) -> bool {
57 match self {
58 CodeGenContext::Loop { .. } => false,
59 CodeGenContext::TryBlock { .. } => true,
60 CodeGenContext::ExceptHandler { parent } => parent.break_crosses_try_closure(),
61 CodeGenContext::Async(inner) => inner.break_crosses_try_closure(),
62 _ => false,
63 }
64 }
65
66 /// Whether the loop a `break` here targets carries a Python `else`
67 /// clause, so the break must also set the loop's `__rython_broke` flag.
68 pub fn break_target_has_else(&self) -> bool {
69 match self {
70 CodeGenContext::Loop { has_else, .. } => *has_else,
71 CodeGenContext::TryBlock { parent }
72 | CodeGenContext::ExceptHandler { parent } => parent.break_target_has_else(),
73 CodeGenContext::Async(inner) => inner.break_target_has_else(),
74 _ => false,
75 }
76 }
77
78 /// Whether code generated here runs inside an except handler, i.e. the
79 /// caught exception is in scope as `__rython_exc` (for bare `raise`).
80 pub fn in_except_handler(&self) -> bool {
81 match self {
82 CodeGenContext::ExceptHandler { .. } => true,
83 CodeGenContext::TryBlock { parent }
84 | CodeGenContext::Loop { parent, .. } => parent.in_except_handler(),
85 CodeGenContext::Async(inner) => inner.in_except_handler(),
86 _ => false,
87 }
88 }
89
90 /// The context for a nested function definition's body: exception scopes
91 /// don't cross function boundaries (a `raise` in a nested function can't
92 /// return out of the enclosing try's closure).
93 pub fn strip_exception_scopes(self) -> CodeGenContext {
94 match self {
95 CodeGenContext::TryBlock { parent }
96 | CodeGenContext::ExceptHandler { parent } => parent.strip_exception_scopes(),
97 other => other,
98 }
99 }
100
101 /// The name of the class whose method body this context sits inside, if
102 /// any — the class `self` refers to.
103 pub fn enclosing_class_name(&self) -> Option<&str> {
104 match self {
105 CodeGenContext::Class(name) => Some(name),
106 CodeGenContext::Async(inner) => inner.enclosing_class_name(),
107 CodeGenContext::Loop { parent, .. }
108 | CodeGenContext::TryBlock { parent }
109 | CodeGenContext::ExceptHandler { parent } => parent.enclosing_class_name(),
110 _ => None,
111 }
112 }
113}