1use crate::python_ast::*;
16use crate::{Error, Result};
17
18pub struct SimplePythonParser {
19 lines: Vec<String>,
20 current_line: usize,
21}
22
23impl SimplePythonParser {
24 pub fn new(source: &str) -> Self {
25 let lines: Vec<String> = source.lines().map(|s| s.to_string()).collect();
26 Self {
27 lines,
28 current_line: 0,
29 }
30 }
31
32 pub fn parse(&mut self) -> Result<PyModule> {
33 let mut module = PyModule::new();
34
35 while self.current_line < self.lines.len() {
36 let line = self.lines[self.current_line].trim();
37
38 if line.is_empty() || line.starts_with('#') {
40 self.current_line += 1;
41 continue;
42 }
43
44 if let Some(stmt) = self.parse_statement(line)? {
46 module.add_stmt(stmt);
47 }
48
49 self.current_line += 1;
50 }
51
52 Ok(module)
53 }
54
55 fn parse_statement(&self, line: &str) -> Result<Option<PyStmt>> {
56 if line.starts_with("if ") && line.ends_with(':') {
58 let condition_str = &line[3..line.len() - 1].trim();
59 let condition = self.parse_expr(condition_str)?;
60
61 return Ok(Some(PyStmt::If {
62 test: condition,
63 body: vec![], orelse: vec![],
65 }));
66 }
67
68 if line == "pass" {
70 return Ok(Some(PyStmt::Pass));
71 }
72
73 if line == "break" {
75 return Ok(Some(PyStmt::Break));
76 }
77
78 if line == "continue" {
80 return Ok(Some(PyStmt::Continue));
81 }
82
83 if let Some(pos) = line.find('=') {
85 if pos > 0 && line.len() > pos + 1 {
87 let before = line.chars().nth(pos.saturating_sub(1));
88 let after = line.chars().nth(pos + 1);
89
90 if !matches!(before, Some('=') | Some('!') | Some('<') | Some('>'))
91 && !matches!(after, Some('='))
92 {
93 let target = line[..pos].trim().to_string();
94 let value_str = line[pos + 1..].trim();
95
96 if let Some(op_char) = target.chars().last() {
98 if matches!(op_char, '+' | '-' | '*' | '/' | '%' | '&' | '|' | '^') {
99 let actual_target = target[..target.len() - 1].trim().to_string();
100 let op = match op_char {
101 '+' => BinOp::Add,
102 '-' => BinOp::Sub,
103 '*' => BinOp::Mult,
104 '/' => BinOp::Div,
105 '%' => BinOp::Mod,
106 '&' => BinOp::BitAnd,
107 '|' => BinOp::BitOr,
108 '^' => BinOp::BitXor,
109 _ => unreachable!(),
110 };
111 let value = self.parse_expr(value_str)?;
112
113 return Ok(Some(PyStmt::AugAssign {
114 target: PyExpr::Name(actual_target),
115 op,
116 value,
117 }));
118 }
119 }
120
121 let value = self.parse_expr(value_str)?;
123 return Ok(Some(PyStmt::Assign {
124 target: PyExpr::Name(target),
125 value,
126 }));
127 }
128 }
129 }
130
131 if line.starts_with("print(") && line.ends_with(')') {
133 let args_str = &line[6..line.len() - 1];
134 let expr = self.parse_expr(args_str)?;
135
136 return Ok(Some(PyStmt::Expr(PyExpr::Call {
137 func: Box::new(PyExpr::Name("print".to_string())),
138 args: vec![expr],
139 kwargs: std::collections::HashMap::new(),
140 })));
141 }
142
143 Ok(None)
144 }
145
146 fn parse_expr(&self, s: &str) -> Result<PyExpr> {
147 let s = s.trim();
148
149 if s == "True" {
151 return Ok(PyExpr::Literal(PyLiteral::Bool(true)));
152 }
153 if s == "False" {
154 return Ok(PyExpr::Literal(PyLiteral::Bool(false)));
155 }
156
157 if s == "None" {
159 return Ok(PyExpr::Literal(PyLiteral::None));
160 }
161
162 if s.starts_with('[') && s.ends_with(']') {
164 let content = &s[1..s.len() - 1].trim();
165 if content.is_empty() {
166 return Ok(PyExpr::List(vec![]));
167 }
168 let elements: Result<Vec<_>> = content
169 .split(',')
170 .map(|e| self.parse_expr(e.trim()))
171 .collect();
172 return Ok(PyExpr::List(elements?));
173 }
174
175 if s.starts_with('(') && s.ends_with(')') {
177 let content = &s[1..s.len() - 1].trim();
178 if content.is_empty() {
179 return Ok(PyExpr::Tuple(vec![]));
180 }
181 let elements: Result<Vec<_>> = content
182 .split(',')
183 .map(|e| self.parse_expr(e.trim()))
184 .collect();
185 return Ok(PyExpr::Tuple(elements?));
186 }
187
188 if (s.starts_with('"') && s.ends_with('"'))
190 || (s.starts_with('\'') && s.ends_with('\''))
191 {
192 let content = &s[1..s.len() - 1];
193 return Ok(PyExpr::Literal(PyLiteral::String(content.to_string())));
194 }
195
196 if s.contains('.') && s.parse::<f64>().is_ok() {
198 let value = s.parse::<f64>().unwrap();
199 return Ok(PyExpr::Literal(PyLiteral::Float(value)));
200 }
201
202 if let Ok(value) = s.parse::<i64>() {
204 return Ok(PyExpr::Literal(PyLiteral::Int(value)));
205 }
206
207 for (op_str, python_op) in &[
209 (" and ", "&&"),
210 (" or ", "||"),
211 ] {
212 if let Some(pos) = s.find(op_str) {
213 let left = self.parse_expr(&s[..pos])?;
214 let right = self.parse_expr(&s[pos + op_str.len()..])?;
215 let op = if *python_op == "&&" {
217 BinOp::BitAnd } else {
219 BinOp::BitOr };
221 return Ok(PyExpr::BinOp {
222 left: Box::new(left),
223 op,
224 right: Box::new(right),
225 });
226 }
227 }
228
229 for (op_str, cmp_op) in &[
231 ("==", CmpOp::Eq),
232 ("!=", CmpOp::NotEq),
233 ("<=", CmpOp::LtE),
234 (">=", CmpOp::GtE),
235 ("<", CmpOp::Lt),
236 (">", CmpOp::Gt),
237 ] {
238 if let Some(pos) = s.find(op_str) {
239 let left = self.parse_expr(&s[..pos])?;
240 let right = self.parse_expr(&s[pos + op_str.len()..])?;
241 return Ok(PyExpr::Compare {
242 left: Box::new(left),
243 op: *cmp_op,
244 right: Box::new(right),
245 });
246 }
247 }
248
249 if s.starts_with("not ") {
251 let operand = self.parse_expr(&s[4..])?;
252 return Ok(PyExpr::UnaryOp {
253 op: UnaryOp::Not,
254 operand: Box::new(operand),
255 });
256 }
257
258 for (op_str, op) in &[
260 (" + ", BinOp::Add),
261 (" - ", BinOp::Sub),
262 (" * ", BinOp::Mult),
263 (" / ", BinOp::Div),
264 (" % ", BinOp::Mod),
265 ] {
266 if let Some(pos) = s.find(op_str) {
267 let left = self.parse_expr(&s[..pos])?;
268 let right = self.parse_expr(&s[pos + op_str.len()..])?;
269 return Ok(PyExpr::BinOp {
270 left: Box::new(left),
271 op: *op,
272 right: Box::new(right),
273 });
274 }
275 }
276
277 if let Some(bracket_pos) = s.find('[') {
279 if s.ends_with(']') {
280 let value_str = &s[..bracket_pos];
281 let index_str = &s[bracket_pos + 1..s.len() - 1];
282 let value = self.parse_expr(value_str)?;
283 let index = self.parse_expr(index_str)?;
284 return Ok(PyExpr::Subscript {
285 value: Box::new(value),
286 index: Box::new(index),
287 });
288 }
289 }
290
291 if s.chars().all(|c| c.is_alphanumeric() || c == '_') {
293 return Ok(PyExpr::Name(s.to_string()));
294 }
295
296 Err(Error::CodeGeneration(format!(
297 "Unable to parse expression: {}",
298 s
299 )))
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_parse_integer_literal() {
309 let source = "x = 42";
310 let mut parser = SimplePythonParser::new(source);
311 let module = parser.parse().unwrap();
312
313 assert_eq!(module.statements.len(), 1);
314 match &module.statements[0] {
315 PyStmt::Assign { target, value } => {
316 assert_eq!(*target, PyExpr::Name("x".to_string()));
317 assert_eq!(*value, PyExpr::Literal(PyLiteral::Int(42)));
318 }
319 _ => panic!("Expected assignment"),
320 }
321 }
322
323 #[test]
324 fn test_parse_float_literal() {
325 let source = "pi = 3.14";
326 let mut parser = SimplePythonParser::new(source);
327 let module = parser.parse().unwrap();
328
329 assert_eq!(module.statements.len(), 1);
330 match &module.statements[0] {
331 PyStmt::Assign { value, .. } => {
332 assert_eq!(*value, PyExpr::Literal(PyLiteral::Float(3.14)));
333 }
334 _ => panic!("Expected assignment"),
335 }
336 }
337
338 #[test]
339 fn test_parse_string_literal() {
340 let source = r#"msg = "hello""#;
341 let mut parser = SimplePythonParser::new(source);
342 let module = parser.parse().unwrap();
343
344 assert_eq!(module.statements.len(), 1);
345 match &module.statements[0] {
346 PyStmt::Assign { value, .. } => {
347 assert_eq!(
348 *value,
349 PyExpr::Literal(PyLiteral::String("hello".to_string()))
350 );
351 }
352 _ => panic!("Expected assignment"),
353 }
354 }
355
356 #[test]
357 fn test_parse_boolean_literal() {
358 let source = "flag = True";
359 let mut parser = SimplePythonParser::new(source);
360 let module = parser.parse().unwrap();
361
362 assert_eq!(module.statements.len(), 1);
363 match &module.statements[0] {
364 PyStmt::Assign { value, .. } => {
365 assert_eq!(*value, PyExpr::Literal(PyLiteral::Bool(true)));
366 }
367 _ => panic!("Expected assignment"),
368 }
369 }
370
371 #[test]
372 fn test_parse_augmented_assignment() {
373 let source = "x += 5";
374 let mut parser = SimplePythonParser::new(source);
375 let module = parser.parse().unwrap();
376
377 assert_eq!(module.statements.len(), 1);
378 match &module.statements[0] {
379 PyStmt::AugAssign { target, op, value } => {
380 assert_eq!(*target, PyExpr::Name("x".to_string()));
381 assert_eq!(*op, BinOp::Add);
382 assert_eq!(*value, PyExpr::Literal(PyLiteral::Int(5)));
383 }
384 _ => panic!("Expected augmented assignment"),
385 }
386 }
387
388 #[test]
389 fn test_parse_print() {
390 let source = r#"print("hello")"#;
391 let mut parser = SimplePythonParser::new(source);
392 let module = parser.parse().unwrap();
393
394 assert_eq!(module.statements.len(), 1);
395 match &module.statements[0] {
396 PyStmt::Expr(PyExpr::Call { func, args, .. }) => {
397 assert_eq!(**func, PyExpr::Name("print".to_string()));
398 assert_eq!(args.len(), 1);
399 }
400 _ => panic!("Expected print call"),
401 }
402 }
403
404 #[test]
405 fn test_skip_comments() {
406 let source = "# This is a comment\nx = 42\n# Another comment";
407 let mut parser = SimplePythonParser::new(source);
408 let module = parser.parse().unwrap();
409
410 assert_eq!(module.statements.len(), 1); }
412
413 #[test]
414 fn test_parse_binary_operation() {
415 let source = "result = 2 + 3";
416 let mut parser = SimplePythonParser::new(source);
417 let module = parser.parse().unwrap();
418
419 assert_eq!(module.statements.len(), 1);
420 match &module.statements[0] {
421 PyStmt::Assign { value, .. } => match value {
422 PyExpr::BinOp { left, op, right } => {
423 assert_eq!(**left, PyExpr::Literal(PyLiteral::Int(2)));
424 assert_eq!(*op, BinOp::Add);
425 assert_eq!(**right, PyExpr::Literal(PyLiteral::Int(3)));
426 }
427 _ => panic!("Expected binary operation"),
428 },
429 _ => panic!("Expected assignment"),
430 }
431 }
432}