1use crate::python_ast::*;
13use crate::{Error, Result};
14use rustpython_parser::{ast, Parse};
15use std::path::Path;
16
17pub struct PythonParser {
19 source: String,
20 filename: String,
21}
22
23#[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 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 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 pub fn parse(&self) -> Result<PyModule> {
75 let parsed = ast::Suite::parse(&self.source, &self.filename).map_err(|e| {
77 Error::Parse(format!("rustpython-parser error: {:?}", e))
78 })?;
79
80 let mut converter = AstConverter::new(&self.source, &self.filename);
82 converter.convert_module(&parsed)
83 }
84
85 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 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 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 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 if i + 1 == line {
125 snippet.push_str(&format!(" | {}^\n", " ".repeat(column.saturating_sub(1))));
126 }
127 }
128
129 snippet
130 }
131}
132
133struct 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 #[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 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 fn convert_stmt(&mut self, stmt: &ast::Stmt) -> Result<PyStmt> {
178 match stmt {
179 ast::Stmt::FunctionDef(func) => {
181 let name = func.name.to_string();
182
183 let params = self.convert_parameters(&func.args)?;
185
186 let return_type = func
188 .returns
189 .as_ref()
190 .map(|expr| self.convert_type_annotation(expr))
191 .transpose()?;
192
193 let mut body = Vec::new();
195 for stmt in &func.body {
196 body.push(self.convert_stmt(stmt)?);
197 }
198
199 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, })
214 }
215
216 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 ast::Stmt::ClassDef(class) => {
249 let name = class.name.to_string();
250
251 let bases = class
253 .bases
254 .iter()
255 .map(|base| self.convert_expr(base))
256 .collect::<Result<Vec<_>>>()?;
257
258 let mut body = Vec::new();
260 for stmt in &class.body {
261 body.push(self.convert_stmt(stmt)?);
262 }
263
264 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 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 ast::Stmt::Assign(assign) => {
292 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 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 ast::Stmt::Expr(expr) => {
315 let value = self.convert_expr(&expr.value)?;
316 Ok(PyStmt::Expr(value))
317 }
318
319 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 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 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 ast::Stmt::Break(_) => Ok(PyStmt::Break),
378
379 ast::Stmt::Continue(_) => Ok(PyStmt::Continue),
381
382 ast::Stmt::Pass(_) => Ok(PyStmt::Pass),
384
385 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 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 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 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 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 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 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 ast::Stmt::Global(global) => {
514 let names = global.names.iter().map(|n| n.to_string()).collect();
515 Ok(PyStmt::Global { names })
516 }
517
518 ast::Stmt::Nonlocal(nonlocal) => {
520 let names = nonlocal.names.iter().map(|n| n.to_string()).collect();
521 Ok(PyStmt::Nonlocal { names })
522 }
523
524 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 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 fn convert_expr(&mut self, expr: &ast::Expr) -> Result<PyExpr> {
561 match expr {
562 ast::Expr::Constant(constant) => {
564 let literal = match &constant.value {
565 ast::Constant::Int(i) => {
566 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 ast::Expr::Name(name) => Ok(PyExpr::Name(name.id.to_string())),
594
595 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 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 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 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 ast::Expr::Subscript(subscript) => {
646 let value = Box::new(self.convert_expr(&subscript.value)?);
647
648 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 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 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 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 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 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 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 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 ast::Expr::Compare(compare) => {
769 let left = Box::new(self.convert_expr(&compare.left)?);
770
771 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 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 ast::Expr::Await(await_expr) => {
802 let value = Box::new(self.convert_expr(&await_expr.value)?);
803 Ok(PyExpr::Await(value))
804 }
805
806 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 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, });
841 }
842
843 Ok(params)
844 }
845
846 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 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 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 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 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 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}