spydecy_hir/
python.rs

1//! Python-specific HIR nodes
2//!
3//! This module defines HIR nodes for Python constructs.
4//! These will be unified with C HIR nodes during cross-layer optimization.
5
6use crate::{metadata::Metadata, types::Type, NodeId, Visibility};
7use serde::{Deserialize, Serialize};
8
9/// Python HIR node
10#[allow(clippy::module_name_repetitions)]
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum PythonHIR {
13    /// Module (top-level)
14    Module {
15        /// Module name
16        name: String,
17        /// Module body
18        body: Vec<PythonHIR>,
19        /// Metadata
20        meta: Metadata,
21    },
22
23    /// Function definition
24    Function {
25        /// Node ID
26        id: NodeId,
27        /// Function name
28        name: String,
29        /// Parameters
30        params: Vec<Parameter>,
31        /// Return type annotation
32        return_type: Option<Type>,
33        /// Function body
34        body: Vec<PythonHIR>,
35        /// Decorators
36        decorators: Vec<String>,
37        /// Visibility
38        visibility: Visibility,
39        /// Metadata
40        meta: Metadata,
41    },
42
43    /// Class definition
44    Class {
45        /// Node ID
46        id: NodeId,
47        /// Class name
48        name: String,
49        /// Base classes
50        bases: Vec<String>,
51        /// Class body
52        body: Vec<PythonHIR>,
53        /// Decorators
54        decorators: Vec<String>,
55        /// Metadata
56        meta: Metadata,
57    },
58
59    /// Function call
60    Call {
61        /// Node ID
62        id: NodeId,
63        /// Function being called
64        callee: Box<PythonHIR>,
65        /// Arguments
66        args: Vec<PythonHIR>,
67        /// Keyword arguments
68        kwargs: Vec<(String, PythonHIR)>,
69        /// Inferred type
70        inferred_type: Option<Type>,
71        /// Metadata
72        meta: Metadata,
73    },
74
75    /// Variable reference
76    Variable {
77        /// Node ID
78        id: NodeId,
79        /// Variable name
80        name: String,
81        /// Inferred type
82        inferred_type: Option<Type>,
83        /// Metadata
84        meta: Metadata,
85    },
86
87    /// Assignment
88    Assign {
89        /// Node ID
90        id: NodeId,
91        /// Target variable
92        target: String,
93        /// Value being assigned
94        value: Box<PythonHIR>,
95        /// Type annotation
96        type_annotation: Option<Type>,
97        /// Metadata
98        meta: Metadata,
99    },
100
101    /// Return statement
102    Return {
103        /// Node ID
104        id: NodeId,
105        /// Return value
106        value: Option<Box<PythonHIR>>,
107        /// Metadata
108        meta: Metadata,
109    },
110
111    /// If statement
112    If {
113        /// Node ID
114        id: NodeId,
115        /// Condition
116        condition: Box<PythonHIR>,
117        /// Then branch
118        then_branch: Vec<PythonHIR>,
119        /// Else branch
120        else_branch: Vec<PythonHIR>,
121        /// Metadata
122        meta: Metadata,
123    },
124
125    /// For loop
126    For {
127        /// Node ID
128        id: NodeId,
129        /// Loop variable
130        target: String,
131        /// Iterable
132        iter: Box<PythonHIR>,
133        /// Loop body
134        body: Vec<PythonHIR>,
135        /// Else clause
136        orelse: Vec<PythonHIR>,
137        /// Metadata
138        meta: Metadata,
139    },
140
141    /// While loop
142    While {
143        /// Node ID
144        id: NodeId,
145        /// Condition
146        condition: Box<PythonHIR>,
147        /// Loop body
148        body: Vec<PythonHIR>,
149        /// Else clause
150        orelse: Vec<PythonHIR>,
151        /// Metadata
152        meta: Metadata,
153    },
154
155    /// Binary operation
156    BinOp {
157        /// Node ID
158        id: NodeId,
159        /// Operator
160        op: BinOp,
161        /// Left operand
162        left: Box<PythonHIR>,
163        /// Right operand
164        right: Box<PythonHIR>,
165        /// Inferred type
166        inferred_type: Option<Type>,
167        /// Metadata
168        meta: Metadata,
169    },
170
171    /// Unary operation
172    UnaryOp {
173        /// Node ID
174        id: NodeId,
175        /// Operator
176        op: UnaryOp,
177        /// Operand
178        operand: Box<PythonHIR>,
179        /// Inferred type
180        inferred_type: Option<Type>,
181        /// Metadata
182        meta: Metadata,
183    },
184
185    /// Literal value
186    Literal {
187        /// Node ID
188        id: NodeId,
189        /// Literal value
190        value: Literal,
191        /// Metadata
192        meta: Metadata,
193    },
194
195    /// List comprehension
196    ListComp {
197        /// Node ID
198        id: NodeId,
199        /// Element expression
200        element: Box<PythonHIR>,
201        /// Generators
202        generators: Vec<Comprehension>,
203        /// Metadata
204        meta: Metadata,
205    },
206
207    /// Attribute access (obj.attr)
208    Attribute {
209        /// Node ID
210        id: NodeId,
211        /// Object
212        object: Box<PythonHIR>,
213        /// Attribute name
214        attr: String,
215        /// Inferred type
216        inferred_type: Option<Type>,
217        /// Metadata
218        meta: Metadata,
219    },
220
221    /// Subscript (obj[index])
222    Subscript {
223        /// Node ID
224        id: NodeId,
225        /// Object
226        object: Box<PythonHIR>,
227        /// Index
228        index: Box<PythonHIR>,
229        /// Inferred type
230        inferred_type: Option<Type>,
231        /// Metadata
232        meta: Metadata,
233    },
234}
235
236/// Function parameter
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238pub struct Parameter {
239    /// Parameter name
240    pub name: String,
241    /// Type annotation
242    pub type_annotation: Option<Type>,
243    /// Default value
244    pub default: Option<String>,
245}
246
247/// Binary operator
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
249pub enum BinOp {
250    /// +
251    Add,
252    /// -
253    Sub,
254    /// *
255    Mul,
256    /// /
257    Div,
258    /// //
259    FloorDiv,
260    /// %
261    Mod,
262    /// **
263    Pow,
264    /// ==
265    Eq,
266    /// !=
267    NotEq,
268    /// <
269    Lt,
270    /// <=
271    Le,
272    /// >
273    Gt,
274    /// >=
275    Ge,
276    /// and
277    And,
278    /// or
279    Or,
280}
281
282/// Unary operator
283#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
284pub enum UnaryOp {
285    /// not
286    Not,
287    /// -
288    Neg,
289    /// +
290    Pos,
291}
292
293/// Literal value
294#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
295pub enum Literal {
296    /// Integer
297    Int(i64),
298    /// Float
299    Float(f64),
300    /// String
301    Str(String),
302    /// Boolean
303    Bool(bool),
304    /// None
305    None,
306}
307
308/// List comprehension generator
309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
310pub struct Comprehension {
311    /// Target variable
312    pub target: String,
313    /// Iterable
314    pub iter: Box<PythonHIR>,
315    /// Filters
316    pub ifs: Vec<PythonHIR>,
317}
318
319impl PythonHIR {
320    /// Get the node ID if present
321    #[must_use]
322    pub const fn id(&self) -> Option<NodeId> {
323        match self {
324            Self::Module { .. } => None,
325            Self::Function { id, .. }
326            | Self::Class { id, .. }
327            | Self::Call { id, .. }
328            | Self::Variable { id, .. }
329            | Self::Assign { id, .. }
330            | Self::Return { id, .. }
331            | Self::If { id, .. }
332            | Self::For { id, .. }
333            | Self::While { id, .. }
334            | Self::BinOp { id, .. }
335            | Self::UnaryOp { id, .. }
336            | Self::Literal { id, .. }
337            | Self::ListComp { id, .. }
338            | Self::Attribute { id, .. }
339            | Self::Subscript { id, .. } => Some(*id),
340        }
341    }
342
343    /// Get the metadata
344    #[must_use]
345    pub const fn metadata(&self) -> &Metadata {
346        match self {
347            Self::Module { meta, .. }
348            | Self::Function { meta, .. }
349            | Self::Class { meta, .. }
350            | Self::Call { meta, .. }
351            | Self::Variable { meta, .. }
352            | Self::Assign { meta, .. }
353            | Self::Return { meta, .. }
354            | Self::If { meta, .. }
355            | Self::For { meta, .. }
356            | Self::While { meta, .. }
357            | Self::BinOp { meta, .. }
358            | Self::UnaryOp { meta, .. }
359            | Self::Literal { meta, .. }
360            | Self::ListComp { meta, .. }
361            | Self::Attribute { meta, .. }
362            | Self::Subscript { meta, .. } => meta,
363        }
364    }
365}
366
367#[cfg(test)]
368mod tests {
369    use super::*;
370
371    #[test]
372    fn test_python_function_creation() {
373        let func = PythonHIR::Function {
374            id: NodeId::new(1),
375            name: "test_func".to_owned(),
376            params: vec![],
377            return_type: None,
378            body: vec![],
379            decorators: vec![],
380            visibility: Visibility::Public,
381            meta: Metadata::new(),
382        };
383
384        assert_eq!(func.id(), Some(NodeId::new(1)));
385    }
386
387    #[test]
388    fn test_python_call_creation() {
389        let call = PythonHIR::Call {
390            id: NodeId::new(2),
391            callee: Box::new(PythonHIR::Variable {
392                id: NodeId::new(3),
393                name: "len".to_owned(),
394                inferred_type: None,
395                meta: Metadata::new(),
396            }),
397            args: vec![],
398            kwargs: vec![],
399            inferred_type: None,
400            meta: Metadata::new(),
401        };
402
403        assert_eq!(call.id(), Some(NodeId::new(2)));
404    }
405}