portalis_transpiler/
python_parser.rs

1//! Full Python AST Parser using rustpython-parser
2//!
3//! This module provides complete Python 3.x parsing capabilities by wrapping
4//! rustpython-parser and converting its AST to our internal representation.
5//!
6//! Key features:
7//! - Full Python 3.10+ syntax support
8//! - Source location tracking (line/column numbers)
9//! - Comprehensive error reporting with context
10//! - Support for all Python constructs (modules, functions, classes, etc.)
11
12use crate::python_ast::*;
13use crate::{Error, Result};
14use rustpython_parser::{ast, Parse};
15use std::path::Path;
16
17/// Main Python parser that wraps rustpython-parser
18pub struct PythonParser {
19    source: String,
20    filename: String,
21}
22
23/// Parse error with source location information
24#[derive(Debug, Clone)]
25pub struct ParseError {
26    pub message: String,
27    pub line: usize,
28    pub column: usize,
29    pub filename: String,
30    pub source_snippet: Option<String>,
31}
32
33impl std::fmt::Display for ParseError {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(
36            f,
37            "Parse error in {} at line {}, column {}: {}",
38            self.filename, self.line, self.column, self.message
39        )?;
40
41        if let Some(snippet) = &self.source_snippet {
42            write!(f, "\n{}", snippet)?;
43        }
44
45        Ok(())
46    }
47}
48
49impl std::error::Error for ParseError {}
50
51impl PythonParser {
52    /// Create a new parser for the given source code
53    pub fn new(source: impl Into<String>, filename: impl Into<String>) -> Self {
54        Self {
55            source: source.into(),
56            filename: filename.into(),
57        }
58    }
59
60    /// Create a parser from a file
61    pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
62        let path = path.as_ref();
63        let source = std::fs::read_to_string(path).map_err(|e| {
64            Error::Parse(format!("Failed to read file {:?}: {}", path, e))
65        })?;
66
67        Ok(Self::new(
68            source,
69            path.to_string_lossy().to_string(),
70        ))
71    }
72
73    /// Parse the source code into a Python module
74    pub fn parse(&self) -> Result<PyModule> {
75        // Parse using rustpython-parser
76        let parsed = ast::Suite::parse(&self.source, &self.filename).map_err(|e| {
77            Error::Parse(format!("rustpython-parser error: {:?}", e))
78        })?;
79
80        // Convert to our internal AST representation
81        let mut converter = AstConverter::new(&self.source, &self.filename);
82        converter.convert_module(&parsed)
83    }
84
85    /// Parse a single expression
86    pub fn parse_expression(&self) -> Result<PyExpr> {
87        let parsed = ast::Expr::parse(&self.source, &self.filename).map_err(|e| {
88            Error::Parse(format!("rustpython-parser error: {:?}", e))
89        })?;
90
91        let mut converter = AstConverter::new(&self.source, &self.filename);
92        converter.convert_expr(&parsed)
93    }
94
95    /// Parse a single statement
96    pub fn parse_statement(&self) -> Result<PyStmt> {
97        let parsed = ast::Suite::parse(&self.source, &self.filename).map_err(|e| {
98            Error::Parse(format!("rustpython-parser error: {:?}", e))
99        })?;
100
101        let mut converter = AstConverter::new(&self.source, &self.filename);
102
103        // Extract first statement from parsed statements
104        if parsed.is_empty() {
105            return Err(Error::Parse("No statements found".to_string()));
106        }
107
108        converter.convert_stmt(&parsed[0])
109    }
110
111    /// Get source code snippet for error reporting
112    pub fn get_source_snippet(&self, line: usize, column: usize, context_lines: usize) -> String {
113        let lines: Vec<&str> = self.source.lines().collect();
114
115        let start_line = line.saturating_sub(context_lines);
116        let end_line = (line + context_lines).min(lines.len());
117
118        let mut snippet = String::new();
119
120        for (i, line_content) in lines.iter().enumerate().skip(start_line).take(end_line - start_line) {
121            snippet.push_str(&format!("{:4} | {}\n", i + 1, line_content));
122
123            // Add error marker
124            if i + 1 == line {
125                snippet.push_str(&format!("     | {}^\n", " ".repeat(column.saturating_sub(1))));
126            }
127        }
128
129        snippet
130    }
131}
132
133/// AST converter from rustpython AST to our internal AST
134struct AstConverter {
135    #[allow(dead_code)]
136    source: String,
137    #[allow(dead_code)]
138    filename: String,
139}
140
141impl AstConverter {
142    fn new(source: &str, filename: &str) -> Self {
143        Self {
144            source: source.to_string(),
145            filename: filename.to_string(),
146        }
147    }
148
149    /// Convert a module from rustpython Mod enum
150    #[allow(dead_code)]
151    fn convert_module_from_mod(&mut self, mod_ast: ast::Mod) -> Result<PyModule> {
152        match mod_ast {
153            ast::Mod::Module(module) => self.convert_module(&module.body),
154            ast::Mod::Expression(expr) => {
155                let mut module = PyModule::new();
156                let converted_expr = self.convert_expr(&expr.body)?;
157                module.add_stmt(PyStmt::Expr(converted_expr));
158                Ok(module)
159            }
160            _ => Err(Error::Parse("Unsupported module type".to_string())),
161        }
162    }
163
164    /// Convert a module (list of statements)
165    fn convert_module(&mut self, stmts: &[ast::Stmt]) -> Result<PyModule> {
166        let mut module = PyModule::new();
167
168        for stmt in stmts {
169            let converted = self.convert_stmt(stmt)?;
170            module.add_stmt(converted);
171        }
172
173        Ok(module)
174    }
175
176    /// Convert a statement
177    fn convert_stmt(&mut self, stmt: &ast::Stmt) -> Result<PyStmt> {
178        match stmt {
179            // Function definition
180            ast::Stmt::FunctionDef(func) => {
181                let name = func.name.to_string();
182
183                // Convert parameters
184                let params = self.convert_parameters(&func.args)?;
185
186                // Convert return type annotation if present
187                let return_type = func
188                    .returns
189                    .as_ref()
190                    .map(|expr| self.convert_type_annotation(expr))
191                    .transpose()?;
192
193                // Convert body
194                let mut body = Vec::new();
195                for stmt in &func.body {
196                    body.push(self.convert_stmt(stmt)?);
197                }
198
199                // Convert decorators
200                let decorators = func
201                    .decorator_list
202                    .iter()
203                    .map(|dec| self.convert_expr(dec))
204                    .collect::<Result<Vec<_>>>()?;
205
206                Ok(PyStmt::FunctionDef {
207                    name,
208                    params,
209                    body,
210                    return_type,
211                    decorators,
212                    is_async: false, // Will be handled by AsyncFunctionDef
213                })
214            }
215
216            // Async function definition
217            ast::Stmt::AsyncFunctionDef(func) => {
218                let name = func.name.to_string();
219                let params = self.convert_parameters(&func.args)?;
220                let return_type = func
221                    .returns
222                    .as_ref()
223                    .map(|expr| self.convert_type_annotation(expr))
224                    .transpose()?;
225
226                let mut body = Vec::new();
227                for stmt in &func.body {
228                    body.push(self.convert_stmt(stmt)?);
229                }
230
231                let decorators = func
232                    .decorator_list
233                    .iter()
234                    .map(|dec| self.convert_expr(dec))
235                    .collect::<Result<Vec<_>>>()?;
236
237                Ok(PyStmt::FunctionDef {
238                    name,
239                    params,
240                    body,
241                    return_type,
242                    decorators,
243                    is_async: true,
244                })
245            }
246
247            // Class definition
248            ast::Stmt::ClassDef(class) => {
249                let name = class.name.to_string();
250
251                // Convert base classes
252                let bases = class
253                    .bases
254                    .iter()
255                    .map(|base| self.convert_expr(base))
256                    .collect::<Result<Vec<_>>>()?;
257
258                // Convert body
259                let mut body = Vec::new();
260                for stmt in &class.body {
261                    body.push(self.convert_stmt(stmt)?);
262                }
263
264                // Convert decorators
265                let decorators = class
266                    .decorator_list
267                    .iter()
268                    .map(|dec| self.convert_expr(dec))
269                    .collect::<Result<Vec<_>>>()?;
270
271                Ok(PyStmt::ClassDef {
272                    name,
273                    bases,
274                    body,
275                    decorators,
276                })
277            }
278
279            // Return statement
280            ast::Stmt::Return(ret) => {
281                let value = ret
282                    .value
283                    .as_ref()
284                    .map(|expr| self.convert_expr(expr))
285                    .transpose()?;
286
287                Ok(PyStmt::Return { value })
288            }
289
290            // Assignment
291            ast::Stmt::Assign(assign) => {
292                // Python allows multiple targets: a = b = c = 42
293                // We'll simplify to single target for now
294                if assign.targets.is_empty() {
295                    return Err(Error::Parse("Assignment with no targets".to_string()));
296                }
297
298                let target = self.convert_expr(&assign.targets[0])?;
299                let value = self.convert_expr(&assign.value)?;
300
301                Ok(PyStmt::Assign { target, value })
302            }
303
304            // Augmented assignment (+=, -=, etc.)
305            ast::Stmt::AugAssign(aug) => {
306                let target = self.convert_expr(&aug.target)?;
307                let op = self.convert_binop(&aug.op);
308                let value = self.convert_expr(&aug.value)?;
309
310                Ok(PyStmt::AugAssign { target, op, value })
311            }
312
313            // Expression statement
314            ast::Stmt::Expr(expr) => {
315                let value = self.convert_expr(&expr.value)?;
316                Ok(PyStmt::Expr(value))
317            }
318
319            // If statement
320            ast::Stmt::If(if_stmt) => {
321                let test = self.convert_expr(&if_stmt.test)?;
322
323                let mut body = Vec::new();
324                for stmt in &if_stmt.body {
325                    body.push(self.convert_stmt(stmt)?);
326                }
327
328                let mut orelse = Vec::new();
329                for stmt in &if_stmt.orelse {
330                    orelse.push(self.convert_stmt(stmt)?);
331                }
332
333                Ok(PyStmt::If { test, body, orelse })
334            }
335
336            // While loop
337            ast::Stmt::While(while_stmt) => {
338                let test = self.convert_expr(&while_stmt.test)?;
339
340                let mut body = Vec::new();
341                for stmt in &while_stmt.body {
342                    body.push(self.convert_stmt(stmt)?);
343                }
344
345                let mut orelse = Vec::new();
346                for stmt in &while_stmt.orelse {
347                    orelse.push(self.convert_stmt(stmt)?);
348                }
349
350                Ok(PyStmt::While { test, body, orelse })
351            }
352
353            // For loop
354            ast::Stmt::For(for_stmt) => {
355                let target = self.convert_expr(&for_stmt.target)?;
356                let iter = self.convert_expr(&for_stmt.iter)?;
357
358                let mut body = Vec::new();
359                for stmt in &for_stmt.body {
360                    body.push(self.convert_stmt(stmt)?);
361                }
362
363                let mut orelse = Vec::new();
364                for stmt in &for_stmt.orelse {
365                    orelse.push(self.convert_stmt(stmt)?);
366                }
367
368                Ok(PyStmt::For {
369                    target,
370                    iter,
371                    body,
372                    orelse,
373                })
374            }
375
376            // Break
377            ast::Stmt::Break(_) => Ok(PyStmt::Break),
378
379            // Continue
380            ast::Stmt::Continue(_) => Ok(PyStmt::Continue),
381
382            // Pass
383            ast::Stmt::Pass(_) => Ok(PyStmt::Pass),
384
385            // Import
386            ast::Stmt::Import(import) => {
387                let mut modules = Vec::new();
388                for alias in &import.names {
389                    modules.push((
390                        alias.name.to_string(),
391                        alias.asname.as_ref().map(|s| s.to_string()),
392                    ));
393                }
394                Ok(PyStmt::Import { modules })
395            }
396
397            // Import from
398            ast::Stmt::ImportFrom(import) => {
399                let module = import.module.as_ref().map(|s| s.to_string());
400
401                let mut names = Vec::new();
402                for alias in &import.names {
403                    names.push((
404                        alias.name.to_string(),
405                        alias.asname.as_ref().map(|s| s.to_string()),
406                    ));
407                }
408
409                Ok(PyStmt::ImportFrom {
410                    module,
411                    names,
412                    level: import.level.map(|i| i.to_usize()).unwrap_or(0),
413                })
414            }
415
416            // Try-except
417            ast::Stmt::Try(try_stmt) => {
418                let mut body = Vec::new();
419                for stmt in &try_stmt.body {
420                    body.push(self.convert_stmt(stmt)?);
421                }
422
423                let mut handlers = Vec::new();
424                for handler in &try_stmt.handlers {
425                    // ExceptHandler is a tuple variant in rustpython_parser
426                    let ast::ExceptHandler::ExceptHandler(handler_data) = handler;
427                    let exception_type = handler_data.type_
428                        .as_ref()
429                        .map(|expr| self.convert_expr(expr))
430                        .transpose()?;
431
432                    let handler_name = handler_data.name.as_ref().map(|id| id.to_string());
433
434                    let mut handler_body = Vec::new();
435                    for stmt in &handler_data.body {
436                        handler_body.push(self.convert_stmt(stmt)?);
437                    }
438
439                    handlers.push(ExceptHandler {
440                        exception_type,
441                        name: handler_name,
442                        body: handler_body,
443                    });
444                }
445
446                let mut orelse = Vec::new();
447                for stmt in &try_stmt.orelse {
448                    orelse.push(self.convert_stmt(stmt)?);
449                }
450
451                let mut finalbody = Vec::new();
452                for stmt in &try_stmt.finalbody {
453                    finalbody.push(self.convert_stmt(stmt)?);
454                }
455
456                Ok(PyStmt::Try {
457                    body,
458                    handlers,
459                    orelse,
460                    finalbody,
461                })
462            }
463
464            // Raise
465            ast::Stmt::Raise(raise) => {
466                let exception = raise
467                    .exc
468                    .as_ref()
469                    .map(|expr| self.convert_expr(expr))
470                    .transpose()?;
471
472                Ok(PyStmt::Raise { exception })
473            }
474
475            // With statement
476            ast::Stmt::With(with) => {
477                let mut items = Vec::new();
478                for item in &with.items {
479                    let context_expr = self.convert_expr(&item.context_expr)?;
480                    let optional_vars = item
481                        .optional_vars
482                        .as_ref()
483                        .map(|expr| self.convert_expr(expr))
484                        .transpose()?;
485
486                    items.push(WithItem {
487                        context_expr,
488                        optional_vars,
489                    });
490                }
491
492                let mut body = Vec::new();
493                for stmt in &with.body {
494                    body.push(self.convert_stmt(stmt)?);
495                }
496
497                Ok(PyStmt::With { items, body })
498            }
499
500            // Assert
501            ast::Stmt::Assert(assert) => {
502                let test = self.convert_expr(&assert.test)?;
503                let msg = assert
504                    .msg
505                    .as_ref()
506                    .map(|expr| self.convert_expr(expr))
507                    .transpose()?;
508
509                Ok(PyStmt::Assert { test, msg })
510            }
511
512            // Global
513            ast::Stmt::Global(global) => {
514                let names = global.names.iter().map(|n| n.to_string()).collect();
515                Ok(PyStmt::Global { names })
516            }
517
518            // Nonlocal
519            ast::Stmt::Nonlocal(nonlocal) => {
520                let names = nonlocal.names.iter().map(|n| n.to_string()).collect();
521                Ok(PyStmt::Nonlocal { names })
522            }
523
524            // Delete
525            ast::Stmt::Delete(delete) => {
526                let targets = delete
527                    .targets
528                    .iter()
529                    .map(|expr| self.convert_expr(expr))
530                    .collect::<Result<Vec<_>>>()?;
531
532                Ok(PyStmt::Delete { targets })
533            }
534
535            // Annotated assignment (PEP 526)
536            ast::Stmt::AnnAssign(ann) => {
537                let target = self.convert_expr(&ann.target)?;
538                let annotation = self.convert_type_annotation(&ann.annotation)?;
539                let value = ann
540                    .value
541                    .as_ref()
542                    .map(|expr| self.convert_expr(expr))
543                    .transpose()?;
544
545                Ok(PyStmt::AnnAssign {
546                    target,
547                    annotation,
548                    value,
549                })
550            }
551
552            _ => Err(Error::Parse(format!(
553                "Unsupported statement type: {:?}",
554                stmt
555            ))),
556        }
557    }
558
559    /// Convert an expression
560    fn convert_expr(&mut self, expr: &ast::Expr) -> Result<PyExpr> {
561        match expr {
562            // Literals
563            ast::Expr::Constant(constant) => {
564                let literal = match &constant.value {
565                    ast::Constant::Int(i) => {
566                        // Try to convert to i64
567                        if let Ok(val) = i.try_into() {
568                            PyLiteral::Int(val)
569                        } else {
570                            return Err(Error::Parse(format!(
571                                "Integer literal too large: {}",
572                                i
573                            )));
574                        }
575                    }
576                    ast::Constant::Float(f) => PyLiteral::Float(*f),
577                    ast::Constant::Str(s) => PyLiteral::String(s.to_string()),
578                    ast::Constant::Bool(b) => PyLiteral::Bool(*b),
579                    ast::Constant::None => PyLiteral::None,
580                    ast::Constant::Bytes(b) => PyLiteral::Bytes(b.clone()),
581                    _ => {
582                        return Err(Error::Parse(format!(
583                            "Unsupported constant type: {:?}",
584                            constant.value
585                        )))
586                    }
587                };
588
589                Ok(PyExpr::Literal(literal))
590            }
591
592            // Variable name
593            ast::Expr::Name(name) => Ok(PyExpr::Name(name.id.to_string())),
594
595            // Binary operation
596            ast::Expr::BinOp(binop) => {
597                let left = Box::new(self.convert_expr(&binop.left)?);
598                let op = self.convert_binop(&binop.op);
599                let right = Box::new(self.convert_expr(&binop.right)?);
600
601                Ok(PyExpr::BinOp { left, op, right })
602            }
603
604            // Unary operation
605            ast::Expr::UnaryOp(unaryop) => {
606                let op = self.convert_unaryop(&unaryop.op);
607                let operand = Box::new(self.convert_expr(&unaryop.operand)?);
608
609                Ok(PyExpr::UnaryOp { op, operand })
610            }
611
612            // Function call
613            ast::Expr::Call(call) => {
614                let func = Box::new(self.convert_expr(&call.func)?);
615
616                let args = call
617                    .args
618                    .iter()
619                    .map(|arg| self.convert_expr(arg))
620                    .collect::<Result<Vec<_>>>()?;
621
622                let mut kwargs = std::collections::HashMap::new();
623                for keyword in &call.keywords {
624                    if let Some(arg_name) = &keyword.arg {
625                        let value = self.convert_expr(&keyword.value)?;
626                        kwargs.insert(arg_name.to_string(), value);
627                    }
628                }
629
630                Ok(PyExpr::Call { func, args, kwargs })
631            }
632
633            // Attribute access
634            ast::Expr::Attribute(attr) => {
635                let value = Box::new(self.convert_expr(&attr.value)?);
636                let attr_name = attr.attr.to_string();
637
638                Ok(PyExpr::Attribute {
639                    value,
640                    attr: attr_name,
641                })
642            }
643
644            // Subscript
645            ast::Expr::Subscript(subscript) => {
646                let value = Box::new(self.convert_expr(&subscript.value)?);
647
648                // Check if it's a slice
649                if let ast::Expr::Slice(slice) = &*subscript.slice {
650                    let lower = match &slice.lower {
651                        Some(expr) => Some(Box::new(self.convert_expr(expr)?)),
652                        None => None,
653                    };
654
655                    let upper = match &slice.upper {
656                        Some(expr) => Some(Box::new(self.convert_expr(expr)?)),
657                        None => None,
658                    };
659
660                    let step = match &slice.step {
661                        Some(expr) => Some(Box::new(self.convert_expr(expr)?)),
662                        None => None,
663                    };
664
665                    Ok(PyExpr::Slice {
666                        value,
667                        lower,
668                        upper,
669                        step,
670                    })
671                } else {
672                    let index = Box::new(self.convert_expr(&subscript.slice)?);
673                    Ok(PyExpr::Subscript { value, index })
674                }
675            }
676
677            // List
678            ast::Expr::List(list) => {
679                let elements = list
680                    .elts
681                    .iter()
682                    .map(|expr| self.convert_expr(expr))
683                    .collect::<Result<Vec<_>>>()?;
684
685                Ok(PyExpr::List(elements))
686            }
687
688            // Tuple
689            ast::Expr::Tuple(tuple) => {
690                let elements = tuple
691                    .elts
692                    .iter()
693                    .map(|expr| self.convert_expr(expr))
694                    .collect::<Result<Vec<_>>>()?;
695
696                Ok(PyExpr::Tuple(elements))
697            }
698
699            // Dict
700            ast::Expr::Dict(dict) => {
701                let keys = dict
702                    .keys
703                    .iter()
704                    .filter_map(|opt_expr| opt_expr.as_ref())
705                    .map(|expr| self.convert_expr(expr))
706                    .collect::<Result<Vec<_>>>()?;
707
708                let values = dict
709                    .values
710                    .iter()
711                    .map(|expr| self.convert_expr(expr))
712                    .collect::<Result<Vec<_>>>()?;
713
714                Ok(PyExpr::Dict { keys, values })
715            }
716
717            // Set
718            ast::Expr::Set(set) => {
719                let elements = set
720                    .elts
721                    .iter()
722                    .map(|expr| self.convert_expr(expr))
723                    .collect::<Result<Vec<_>>>()?;
724
725                Ok(PyExpr::Set(elements))
726            }
727
728            // List comprehension
729            ast::Expr::ListComp(comp) => {
730                let element = Box::new(self.convert_expr(&comp.elt)?);
731
732                let generators = comp
733                    .generators
734                    .iter()
735                    .map(|gen| self.convert_comprehension(gen))
736                    .collect::<Result<Vec<_>>>()?;
737
738                Ok(PyExpr::ListComp {
739                    element,
740                    generators,
741                })
742            }
743
744            // Conditional expression (ternary)
745            ast::Expr::IfExp(ifexp) => {
746                let test = Box::new(self.convert_expr(&ifexp.test)?);
747                let body = Box::new(self.convert_expr(&ifexp.body)?);
748                let orelse = Box::new(self.convert_expr(&ifexp.orelse)?);
749
750                Ok(PyExpr::IfExp { test, body, orelse })
751            }
752
753            // Lambda
754            ast::Expr::Lambda(lambda) => {
755                let args = lambda
756                    .args
757                    .args
758                    .iter()
759                    .map(|arg| arg.def.arg.to_string())
760                    .collect();
761
762                let body = Box::new(self.convert_expr(&lambda.body)?);
763
764                Ok(PyExpr::Lambda { args, body })
765            }
766
767            // Comparison
768            ast::Expr::Compare(compare) => {
769                let left = Box::new(self.convert_expr(&compare.left)?);
770
771                // Python allows chained comparisons: a < b < c
772                // For simplicity, we'll handle the first comparison only
773                if compare.ops.is_empty() || compare.comparators.is_empty() {
774                    return Err(Error::Parse("Empty comparison".to_string()));
775                }
776
777                let op = self.convert_cmpop(&compare.ops[0]);
778                let right = Box::new(self.convert_expr(&compare.comparators[0])?);
779
780                Ok(PyExpr::Compare { left, op, right })
781            }
782
783            // Boolean operation (and, or)
784            ast::Expr::BoolOp(boolop) => {
785                if boolop.values.len() < 2 {
786                    return Err(Error::Parse("BoolOp with less than 2 values".to_string()));
787                }
788
789                let op = match boolop.op {
790                    ast::BoolOp::And => BoolOp::And,
791                    ast::BoolOp::Or => BoolOp::Or,
792                };
793
794                let left = Box::new(self.convert_expr(&boolop.values[0])?);
795                let right = Box::new(self.convert_expr(&boolop.values[1])?);
796
797                Ok(PyExpr::BoolOp { op, left, right })
798            }
799
800            // Await expression
801            ast::Expr::Await(await_expr) => {
802                let value = Box::new(self.convert_expr(&await_expr.value)?);
803                Ok(PyExpr::Await(value))
804            }
805
806            // Yield expression
807            ast::Expr::Yield(yield_expr) => {
808                let value = match &yield_expr.value {
809                    Some(expr) => Some(Box::new(self.convert_expr(expr)?)),
810                    None => None,
811                };
812
813                Ok(PyExpr::Yield(value))
814            }
815
816            _ => Err(Error::Parse(format!(
817                "Unsupported expression type: {:?}",
818                expr
819            ))),
820        }
821    }
822
823    /// Convert function parameters
824    fn convert_parameters(&mut self, args: &ast::Arguments) -> Result<Vec<FunctionParam>> {
825        let mut params = Vec::new();
826
827        for arg in &args.args {
828            let name = arg.def.arg.to_string();
829            let type_annotation = arg
830                .def
831                .annotation
832                .as_ref()
833                .map(|expr| self.convert_type_annotation(expr))
834                .transpose()?;
835
836            params.push(FunctionParam {
837                name,
838                type_annotation,
839                default_value: None, // Defaults are handled separately in rustpython
840            });
841        }
842
843        Ok(params)
844    }
845
846    /// Convert type annotation
847    fn convert_type_annotation(&mut self, expr: &ast::Expr) -> Result<TypeAnnotation> {
848        match expr {
849            ast::Expr::Name(name) => Ok(TypeAnnotation::Name(name.id.to_string())),
850
851            ast::Expr::Subscript(subscript) => {
852                // Generic types like List[int], Dict[str, int]
853                if let ast::Expr::Name(name) = &*subscript.value {
854                    let base = name.id.to_string();
855                    let args = vec![self.convert_type_annotation(&subscript.slice)?];
856
857                    Ok(TypeAnnotation::Generic {
858                        base: Box::new(TypeAnnotation::Name(base)),
859                        args,
860                    })
861                } else {
862                    Err(Error::Parse("Complex generic type".to_string()))
863                }
864            }
865
866            _ => Ok(TypeAnnotation::Name("Any".to_string())),
867        }
868    }
869
870    /// Convert comprehension generator
871    fn convert_comprehension(&mut self, gen: &ast::Comprehension) -> Result<Comprehension> {
872        let target = self.convert_expr(&gen.target)?;
873        let iter = self.convert_expr(&gen.iter)?;
874
875        let ifs = gen
876            .ifs
877            .iter()
878            .map(|expr| self.convert_expr(expr))
879            .collect::<Result<Vec<_>>>()?;
880
881        Ok(Comprehension { target, iter, ifs })
882    }
883
884    /// Convert binary operator
885    fn convert_binop(&self, op: &ast::Operator) -> BinOp {
886        match op {
887            ast::Operator::Add => BinOp::Add,
888            ast::Operator::Sub => BinOp::Sub,
889            ast::Operator::Mult => BinOp::Mult,
890            ast::Operator::Div => BinOp::Div,
891            ast::Operator::FloorDiv => BinOp::FloorDiv,
892            ast::Operator::Mod => BinOp::Mod,
893            ast::Operator::Pow => BinOp::Pow,
894            ast::Operator::LShift => BinOp::LShift,
895            ast::Operator::RShift => BinOp::RShift,
896            ast::Operator::BitOr => BinOp::BitOr,
897            ast::Operator::BitXor => BinOp::BitXor,
898            ast::Operator::BitAnd => BinOp::BitAnd,
899            ast::Operator::MatMult => BinOp::MatMult,
900        }
901    }
902
903    /// Convert unary operator
904    fn convert_unaryop(&self, op: &ast::UnaryOp) -> UnaryOp {
905        match op {
906            ast::UnaryOp::Not => UnaryOp::Not,
907            ast::UnaryOp::UAdd => UnaryOp::UAdd,
908            ast::UnaryOp::USub => UnaryOp::USub,
909            ast::UnaryOp::Invert => UnaryOp::Invert,
910        }
911    }
912
913    /// Convert comparison operator
914    fn convert_cmpop(&self, op: &ast::CmpOp) -> CmpOp {
915        match op {
916            ast::CmpOp::Eq => CmpOp::Eq,
917            ast::CmpOp::NotEq => CmpOp::NotEq,
918            ast::CmpOp::Lt => CmpOp::Lt,
919            ast::CmpOp::LtE => CmpOp::LtE,
920            ast::CmpOp::Gt => CmpOp::Gt,
921            ast::CmpOp::GtE => CmpOp::GtE,
922            ast::CmpOp::Is => CmpOp::Is,
923            ast::CmpOp::IsNot => CmpOp::IsNot,
924            ast::CmpOp::In => CmpOp::In,
925            ast::CmpOp::NotIn => CmpOp::NotIn,
926        }
927    }
928}
929
930#[cfg(test)]
931mod tests {
932    use super::*;
933
934    #[test]
935    fn test_parse_simple_function() {
936        let source = r#"
937def add(a, b):
938    return a + b
939"#;
940
941        let parser = PythonParser::new(source, "test.py");
942        let module = parser.parse().unwrap();
943
944        assert_eq!(module.statements.len(), 1);
945
946        if let PyStmt::FunctionDef { name, params, .. } = &module.statements[0] {
947            assert_eq!(name, "add");
948            assert_eq!(params.len(), 2);
949        } else {
950            panic!("Expected function definition");
951        }
952    }
953
954    #[test]
955    fn test_parse_literals() {
956        let source = "42";
957        let parser = PythonParser::new(source, "test.py");
958        let expr = parser.parse_expression().unwrap();
959
960        assert!(matches!(expr, PyExpr::Literal(PyLiteral::Int(42))));
961
962        let source = "3.14";
963        let parser = PythonParser::new(source, "test.py");
964        let expr = parser.parse_expression().unwrap();
965
966        assert!(matches!(expr, PyExpr::Literal(PyLiteral::Float(_))));
967
968        let source = r#""hello""#;
969        let parser = PythonParser::new(source, "test.py");
970        let expr = parser.parse_expression().unwrap();
971
972        assert!(matches!(expr, PyExpr::Literal(PyLiteral::String(_))));
973    }
974
975    #[test]
976    fn test_parse_binary_operation() {
977        let source = "a + b";
978        let parser = PythonParser::new(source, "test.py");
979        let expr = parser.parse_expression().unwrap();
980
981        if let PyExpr::BinOp { left, op, right } = expr {
982            assert!(matches!(*left, PyExpr::Name(_)));
983            assert!(matches!(op, BinOp::Add));
984            assert!(matches!(*right, PyExpr::Name(_)));
985        } else {
986            panic!("Expected binary operation");
987        }
988    }
989
990    #[test]
991    fn test_parse_function_call() {
992        let source = "print(42)";
993        let parser = PythonParser::new(source, "test.py");
994        let expr = parser.parse_expression().unwrap();
995
996        if let PyExpr::Call { func, args, .. } = expr {
997            assert!(matches!(*func, PyExpr::Name(_)));
998            assert_eq!(args.len(), 1);
999        } else {
1000            panic!("Expected function call");
1001        }
1002    }
1003
1004    #[test]
1005    fn test_parse_list_comprehension() {
1006        let source = "[x * 2 for x in range(10)]";
1007        let parser = PythonParser::new(source, "test.py");
1008        let expr = parser.parse_expression().unwrap();
1009
1010        assert!(matches!(expr, PyExpr::ListComp { .. }));
1011    }
1012
1013    #[test]
1014    fn test_parse_if_statement() {
1015        let source = r#"
1016if x > 0:
1017    print("positive")
1018else:
1019    print("negative")
1020"#;
1021
1022        let parser = PythonParser::new(source, "test.py");
1023        let module = parser.parse().unwrap();
1024
1025        assert_eq!(module.statements.len(), 1);
1026        assert!(matches!(module.statements[0], PyStmt::If { .. }));
1027    }
1028
1029    #[test]
1030    fn test_parse_class_definition() {
1031        let source = r#"
1032class MyClass:
1033    def method(self):
1034        pass
1035"#;
1036
1037        let parser = PythonParser::new(source, "test.py");
1038        let module = parser.parse().unwrap();
1039
1040        assert_eq!(module.statements.len(), 1);
1041
1042        if let PyStmt::ClassDef { name, body, .. } = &module.statements[0] {
1043            assert_eq!(name, "MyClass");
1044            assert!(!body.is_empty());
1045        } else {
1046            panic!("Expected class definition");
1047        }
1048    }
1049
1050    #[test]
1051    fn test_parse_import() {
1052        let source = "import sys";
1053        let parser = PythonParser::new(source, "test.py");
1054        let module = parser.parse().unwrap();
1055
1056        assert_eq!(module.statements.len(), 1);
1057        assert!(matches!(module.statements[0], PyStmt::Import { .. }));
1058
1059        let source = "from os import path";
1060        let parser = PythonParser::new(source, "test.py");
1061        let module = parser.parse().unwrap();
1062
1063        assert_eq!(module.statements.len(), 1);
1064        assert!(matches!(module.statements[0], PyStmt::ImportFrom { .. }));
1065    }
1066
1067    #[test]
1068    fn test_parse_async_function() {
1069        let source = r#"
1070async def fetch_data():
1071    result = await get_data()
1072    return result
1073"#;
1074
1075        let parser = PythonParser::new(source, "test.py");
1076        let module = parser.parse().unwrap();
1077
1078        assert_eq!(module.statements.len(), 1);
1079
1080        if let PyStmt::FunctionDef { is_async, .. } = &module.statements[0] {
1081            assert!(is_async);
1082        } else {
1083            panic!("Expected async function definition");
1084        }
1085    }
1086
1087    #[test]
1088    fn test_parse_try_except() {
1089        let source = r#"
1090try:
1091    risky_operation()
1092except ValueError as e:
1093    handle_error(e)
1094finally:
1095    cleanup()
1096"#;
1097
1098        let parser = PythonParser::new(source, "test.py");
1099        let module = parser.parse().unwrap();
1100
1101        assert_eq!(module.statements.len(), 1);
1102        assert!(matches!(module.statements[0], PyStmt::Try { .. }));
1103    }
1104
1105    #[test]
1106    fn test_parse_with_statement() {
1107        let source = r#"
1108with open("file.txt") as f:
1109    content = f.read()
1110"#;
1111
1112        let parser = PythonParser::new(source, "test.py");
1113        let module = parser.parse().unwrap();
1114
1115        assert_eq!(module.statements.len(), 1);
1116        assert!(matches!(module.statements[0], PyStmt::With { .. }));
1117    }
1118
1119    #[test]
1120    fn test_parse_type_annotations() {
1121        let source = r#"
1122def add(a: int, b: int) -> int:
1123    return a + b
1124"#;
1125
1126        let parser = PythonParser::new(source, "test.py");
1127        let module = parser.parse().unwrap();
1128
1129        if let PyStmt::FunctionDef {
1130            params,
1131            return_type,
1132            ..
1133        } = &module.statements[0]
1134        {
1135            assert!(params[0].type_annotation.is_some());
1136            assert!(return_type.is_some());
1137        } else {
1138            panic!("Expected function definition");
1139        }
1140    }
1141}