ale_python_parser/
lib.rs

1use aleph_syntax_tree::syntax::AlephTree as at;
2use rustpython_parser::ast::{
3    Expr, Stmt, BoolOp, CmpOp, Operator, UnaryOp, Arguments, ExprName, StmtReturn,
4    ExprBoolOp, ExprBinOp, ExprUnaryOp, ExprLambda, ExprIfExp, ExprCompare, ExprCall,
5    StmtFunctionDef, StmtAsyncFunctionDef, StmtClassDef, StmtFor, StmtAsyncFor, StmtWhile,
6    StmtIf, StmtAssert, StmtImport, StmtExpr
7};
8use rustpython_parser::Parse;
9
10/// Extracts a constant value from a Python `Constant` into an `AlephTree` node.
11fn extract_constant(value: rustpython_parser::ast::Constant) -> at {
12    match value {
13        rustpython_parser::ast::Constant::None => at::Unit,
14        rustpython_parser::ast::Constant::Bool(b) => at::Bool { value: b.to_string() },
15        rustpython_parser::ast::Constant::Str(s) => at::String { value: format!("\"{}\"", s) },
16        rustpython_parser::ast::Constant::Bytes(b) => at::Bytes { elems: b },
17        rustpython_parser::ast::Constant::Int(i) => at::Int { value: i.to_string() },
18        rustpython_parser::ast::Constant::Tuple(v) => at::Tuple {
19            elems: v.into_iter().map(|c| Box::new(extract_constant(c))).collect(),
20        },
21        rustpython_parser::ast::Constant::Float(f) => at::Float { value: f.to_string() },
22        rustpython_parser::ast::Constant::Complex { real, imag } => at::Complex {
23            real: real.to_string(),
24            imag: imag.to_string(),
25        },
26        rustpython_parser::ast::Constant::Ellipsis => at::Ellipsis,
27    }
28}
29
30/// Extracts the identifier string from a Python `Expr::Name` node.
31fn extract_name(expr: &Expr) -> String {
32    match expr {
33        Expr::Name(ExprName { id, .. }) => id.to_string(),
34        _ => {
35            println!("Not implemented: extract_name for {:?}", expr);
36            String::new()
37        }
38    }
39}
40
41/// Translates Python function arguments into a vector of `AlephTree` nodes.
42/// Currently a stub, returns an empty vector.
43fn translate_arguments_vec(_args: &Arguments) -> Vec<Box<at>> {
44    Vec::new()
45}
46
47/// Translates a Python expression AST node into an `AlephTree`.
48fn translate_expr(expr: &Expr) -> at {
49    match expr {
50        Expr::BoolOp(ExprBoolOp { op, values, .. }) => {
51            let mut res = at::Unit;
52            for value in values {
53                res = match op {
54                    BoolOp::And => match res {
55                        at::Unit => translate_expr(value),
56                        _ => at::And {
57                            bool_expr1: Box::new(res),
58                            bool_expr2: Box::new(translate_expr(value)),
59                        },
60                    },
61                    BoolOp::Or => match res {
62                        at::Unit => translate_expr(value),
63                        _ => at::Or {
64                            bool_expr1: Box::new(res),
65                            bool_expr2: Box::new(translate_expr(value)),
66                        },
67                    },
68                }
69            }
70            res
71        }
72        Expr::BinOp(ExprBinOp { left, op, right, .. }) => match op {
73            Operator::Add => at::Add {
74                number_expr1: Box::new(translate_expr(left)),
75                number_expr2: Box::new(translate_expr(right)),
76            },
77            Operator::Sub => at::Sub {
78                number_expr1: Box::new(translate_expr(left)),
79                number_expr2: Box::new(translate_expr(right)),
80            },
81            Operator::Mult => at::Mul {
82                number_expr1: Box::new(translate_expr(left)),
83                number_expr2: Box::new(translate_expr(right)),
84            },
85            Operator::Div => at::Div {
86                number_expr1: Box::new(translate_expr(left)),
87                number_expr2: Box::new(translate_expr(right)),
88            },
89            _ => {
90                println!("Not implemented: BinOp {:?} {:?} {:?}", left, op, right);
91                at::Unit
92            }
93        },
94        Expr::UnaryOp(ExprUnaryOp { op, operand, .. }) => match op {
95            UnaryOp::Not => at::Not {
96                bool_expr: Box::new(translate_expr(operand)),
97            },
98            UnaryOp::USub => at::Neg {
99                expr: Box::new(translate_expr(operand)),
100            },
101            _ => {
102                println!("Not implemented: UnaryOp {:?} {:?}", op, operand);
103                at::Unit
104            }
105        },
106        Expr::Lambda(ExprLambda { args, body, .. }) => at::LetRec {
107            name: "lambda".to_string(),
108            args: translate_arguments_vec(args),
109            body: Box::new(translate_expr(body)),
110        },
111        Expr::IfExp(ExprIfExp { test, body, orelse, .. }) => at::If {
112            condition: Box::new(translate_expr(test)),
113            then: Box::new(translate_expr(body)),
114            els: Box::new(translate_expr(orelse)),
115        },
116        Expr::Compare(ExprCompare { left, ops, comparators, .. }) => {
117            let mut res = translate_expr(left);
118            for (op, right) in ops.iter().zip(comparators.iter()) {
119                let right_expr = translate_expr(right);
120                res = match op {
121                    CmpOp::Eq => at::Eq {
122                        expr1: Box::new(res),
123                        expr2: Box::new(right_expr),
124                    },
125                    CmpOp::NotEq => at::Not {
126                        bool_expr: Box::new(at::Eq {
127                            expr1: Box::new(res),
128                            expr2: Box::new(right_expr),
129                        }),
130                    },
131                    CmpOp::Lt => at::And {
132                        bool_expr1: Box::new(at::LE {
133                            expr1: Box::new(res.clone()),
134                            expr2: Box::new(right_expr.clone()),
135                        }),
136                        bool_expr2: Box::new(at::Not {
137                            bool_expr: Box::new(at::Eq {
138                                expr1: Box::new(res.clone()),
139                                expr2: Box::new(right_expr.clone()),
140                            }),
141                        }),
142                    },
143                    CmpOp::LtE => at::LE {
144                        expr1: Box::new(res),
145                        expr2: Box::new(right_expr),
146                    },
147                    CmpOp::Gt => at::Not {
148                        bool_expr: Box::new(at::LE {
149                            expr1: Box::new(res),
150                            expr2: Box::new(right_expr.clone()),
151                        }),
152                    },
153                    CmpOp::GtE => at::Or {
154                        bool_expr1: Box::new(at::LE {
155                            expr1: Box::new(res.clone()),
156                            expr2: Box::new(right_expr.clone()),
157                        }),
158                        bool_expr2: Box::new(at::Eq {
159                            expr1: Box::new(res.clone()),
160                            expr2: Box::new(right_expr.clone()),
161                        }),
162                    },
163                    CmpOp::Is => at::Eq {
164                        expr1: Box::new(res),
165                        expr2: Box::new(right_expr),
166                    },
167                    CmpOp::IsNot => at::Not {
168                        bool_expr: Box::new(at::Eq {
169                            expr1: Box::new(res),
170                            expr2: Box::new(right_expr),
171                        }),
172                    },
173                    CmpOp::In => at::In {
174                        expr1: Box::new(res),
175                        expr2: Box::new(right_expr),
176                    },
177                    CmpOp::NotIn => at::Not {
178                        bool_expr: Box::new(at::In {
179                            expr1: Box::new(res),
180                            expr2: Box::new(right_expr),
181                        }),
182                    },
183                }
184            }
185            res
186        }
187        Expr::Call(ExprCall { func, args, .. }) => {
188            let name = extract_name(func);
189            let param_list = args.iter().map(|arg| Box::new(translate_expr(arg))).collect();
190            at::App {
191                object_name: "".to_string(),
192                fun: Box::new(at::String { value: name }),
193                param_list,
194            }
195        }
196        Expr::Constant(expr_constant) => extract_constant(expr_constant.value.clone()),
197        Expr::Name(ExprName { id, .. }) => at::Ident { value: id.to_string() },
198        _ => {
199            println!("Not implemented expression {:?}", expr);
200            at::Unit
201        }
202    }
203}
204
205/// Translates a Python statement AST node into an `AlephTree`.
206fn translate_stmt(stmt: &Stmt) -> at {
207    match stmt {
208        Stmt::FunctionDef(StmtFunctionDef { name, args, body, .. }) |
209        Stmt::AsyncFunctionDef(StmtAsyncFunctionDef { name, args, body, .. }) => at::LetRec {
210            name: name.to_string(),
211            args: translate_arguments_vec(args),
212            body: Box::new(translate_stmt_list(body)),
213        },
214        Stmt::ClassDef(StmtClassDef { name, body, .. }) => at::Clss {
215            name: name.to_string(),
216            attribute_list: Vec::new(),
217            extends: None,
218            implements: Vec::new(),
219            body: Box::new(translate_stmt_list(body)),
220        },
221        Stmt::Return(StmtReturn { value, .. }) => match value {
222            None => at::Unit,
223            Some(expr) => at::Return {
224                value: Box::new(translate_expr(expr)),
225            },
226        },
227        Stmt::For(StmtFor { target, iter, body, orelse, .. }) |
228        Stmt::AsyncFor(StmtAsyncFor { target, iter, body, orelse, .. }) => at::While {
229            init_expr: Box::new(translate_expr(target)),
230            condition: Box::new(translate_expr(iter)),
231            loop_expr: Box::new(translate_stmt_list(body)),
232            post_expr: Box::new(translate_stmt_list(orelse)),
233        },
234        Stmt::While(StmtWhile { test, body, orelse, .. }) => at::While {
235            init_expr: Box::new(at::Unit),
236            condition: Box::new(translate_expr(test)),
237            loop_expr: Box::new(translate_stmt_list(body)),
238            post_expr: Box::new(translate_stmt_list(orelse)),
239        },
240        Stmt::If(StmtIf { test, body, orelse, .. }) => at::If {
241            condition: Box::new(translate_expr(test)),
242            then: Box::new(translate_stmt_list(body)),
243            els: Box::new(translate_stmt_list(orelse)),
244        },
245        Stmt::Assert(StmtAssert { test, msg, .. }) => at::Assert {
246            condition: Box::new(translate_expr(test)),
247            message: msg
248                .as_ref()
249                .map_or_else(|| Box::new(at::Unit), |m| Box::new(translate_expr(m))),
250        },
251        Stmt::Import(StmtImport { names, .. }) => {
252            let items = names.iter().map(|alias| alias.name.to_string()).collect();
253            at::Iprt {
254                name: "".to_string(),
255                items,
256            }
257        }
258        Stmt::Expr(StmtExpr { value, .. }) => translate_expr(value),
259        Stmt::Pass(_) => at::Unit,
260        Stmt::Break(_) => at::Break,
261        Stmt::Continue(_) => at::Continue,
262        _ => {
263            println!("Not implemented statement {:?}", stmt);
264            at::Unit
265        }
266    }
267}
268
269/// Translates a list of Python statements into a sequence of `AlephTree` nodes.
270fn translate_stmt_list(stmts: &[Stmt]) -> at {
271    let mut res = at::Unit;
272    for stmt in stmts {
273        let current = translate_stmt(stmt);
274        res = match res {
275            at::Unit => current,
276            _ => at::Stmts {
277                expr1: Box::new(res),
278                expr2: Box::new(current),
279            },
280        };
281    }
282    res
283}
284
285/// Parses a Python source string into an `AlephTree`.
286pub fn python_parse(source: String) -> at {
287    let ast = rustpython_parser::ast::Suite::parse(&source, "<embedded>")
288        .expect("Failed to parse Python source.");
289    translate_stmt_list(&ast)
290}
291