1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, Bound, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods, types::PyTypeMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 dump, err_from, extraction_failure, Attribute, Await, BinOp, BoolOp, Call, CodeGen, CodeGenContext, Compare,
8 Constant, Dict, DictComp, ExprTypeNotYetImplemented, FormattedValue, GeneratorExp, IfExp,
9 JoinedStr, Lambda, ListComp, Name, NamedExpr, Node, PythonOptions, Set, SetComp, Starred,
10 Subscript, SymbolTableScopes, Tuple, UnaryOp, Yield, YieldFrom,
11};
12
13#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
15#[repr(transparent)]
16pub struct Container<T>(pub T);
17
18impl<'a, 'py> FromPyObject<'a, 'py> for Container<crate::pytypes::List<ExprType>> {
19 type Error = pyo3::PyErr;
20 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
21 let list = crate::pytypes::List::<ExprType>::new();
22
23 tracing::debug!("pylist: {}", dump(&ob, Some(4))?);
24 let _converted_list: Vec<Bound<PyAny>> = ob.extract()?;
25 for item in _converted_list.iter() {
26 tracing::debug!("item: {:?}", item);
27 }
28
29 Ok(Self(list))
30 }
31}
32
33#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
34pub enum ExprType {
35 BoolOp(BoolOp),
36 NamedExpr(NamedExpr),
37 BinOp(BinOp),
38 UnaryOp(UnaryOp),
39 Lambda(Lambda),
40 IfExp(IfExp),
41 Dict(Dict),
42 Set(Set),
43 ListComp(ListComp),
44 DictComp(DictComp),
45 SetComp(SetComp),
46 GeneratorExp(GeneratorExp),
47 Await(Await),
48 Yield(Yield),
49 YieldFrom(YieldFrom),
50 Compare(Compare),
51 Call(Call),
52 FormattedValue(FormattedValue),
53 JoinedStr(JoinedStr),
54 Constant(Constant),
55
56 Attribute(Attribute),
58 Subscript(Subscript),
59 Starred(Starred),
60 Name(Name),
61 List(Vec<ExprType>),
62 Tuple(Tuple),
63 NoneType(Constant),
65
66 Unimplemented(String),
67 #[default]
68 Unknown,
69}
70
71impl<'a, 'py> FromPyObject<'a, 'py> for ExprType {
72 type Error = pyo3::PyErr;
73 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
74 tracing::debug!("exprtype ob: {}", dump(&ob, Some(4))?);
75
76 let expr_type = ob
77 .get_type()
78 .name()
79 .map_err(|e| extraction_failure("expression type name", &ob, e))?;
80 tracing::debug!("expression type: {}, value: {}", expr_type, dump(&ob, None)?);
81
82 let r = match expr_type.extract::<String>()?.as_str() {
83 "Attribute" => {
84 let a = ob.extract().map_err(|e| extraction_failure("extracting Attribute in expression", &ob, e))?;
85 Ok(Self::Attribute(a))
86 }
87 "Await" => {
88 let a = ob.extract().map_err(|e| extraction_failure("extracting await value in expression", &ob, e))?;
90 Ok(Self::Await(a))
91 }
92 "BoolOp" => {
93 let b = ob.extract().map_err(|e| extraction_failure("extracting BoolOp in expression", &ob, e))?;
94 Ok(Self::BoolOp(b))
95 }
96 "Call" => {
97 let et = ob.extract().map_err(|e| extraction_failure("parsing Call expression", &ob, e))?;
98 Ok(Self::Call(et))
99 }
100 "Compare" => {
101 let c = ob.extract().map_err(|e| extraction_failure("extracting Compare in expression", &ob, e))?;
102 Ok(Self::Compare(c))
103 }
104 "Constant" => {
105 tracing::debug!("constant: {}", dump(&ob, None)?);
106 let c = ob.extract().map_err(|e| extraction_failure("extracting Constant in expression", &ob, e))?;
107 Ok(Self::Constant(c))
108 }
109 "List" => {
110 let elts_attr = ob
112 .getattr("elts")
113 .map_err(|e| extraction_failure("list elements", &ob, e))?;
114 let elts_vec: Vec<Bound<PyAny>> = elts_attr
115 .extract()
116 .map_err(|e| extraction_failure("list elements", &ob, e))?;
117
118 let mut expr_list = Vec::new();
120 for elt in elts_vec {
121 let expr: ExprType = elt
122 .extract()
123 .map_err(|e| extraction_failure("list element", &elt, e))?;
124 expr_list.push(expr);
125 }
126
127 Ok(Self::List(expr_list))
128 }
129 "ListComp" => {
130 let lc = ob.extract().map_err(|e| extraction_failure("extracting ListComp in expression", &ob, e))?;
131 Ok(Self::ListComp(lc))
132 }
133 "DictComp" => {
134 let dc = ob.extract().map_err(|e| extraction_failure("extracting DictComp in expression", &ob, e))?;
135 Ok(Self::DictComp(dc))
136 }
137 "SetComp" => {
138 let sc = ob.extract().map_err(|e| extraction_failure("extracting SetComp in expression", &ob, e))?;
139 Ok(Self::SetComp(sc))
140 }
141 "GeneratorExp" => {
142 let ge = ob.extract().map_err(|e| extraction_failure("extracting GeneratorExp in expression", &ob, e))?;
143 Ok(Self::GeneratorExp(ge))
144 }
145 "Name" => {
146 let name = ob.extract().map_err(|e| extraction_failure("parsing Name expression", &ob, e))?;
147 Ok(Self::Name(name))
148 }
149 "UnaryOp" => {
150 let c = ob.extract().map_err(|e| extraction_failure("extracting UnaryOp in expression", &ob, e))?;
151 Ok(Self::UnaryOp(c))
152 }
153 "BinOp" => {
154 let c = ob.extract().map_err(|e| extraction_failure("extracting BinOp in expression", &ob, e))?;
155 Ok(Self::BinOp(c))
156 }
157 "Lambda" => {
158 let l = ob.extract().map_err(|e| extraction_failure("extracting Lambda in expression", &ob, e))?;
159 Ok(Self::Lambda(l))
160 }
161 "IfExp" => {
162 let i = ob.extract().map_err(|e| extraction_failure("extracting IfExp in expression", &ob, e))?;
163 Ok(Self::IfExp(i))
164 }
165 "Dict" => {
166 let d = ob.extract().map_err(|e| extraction_failure("extracting Dict in expression", &ob, e))?;
167 Ok(Self::Dict(d))
168 }
169 "Set" => {
170 let s = ob.extract().map_err(|e| extraction_failure("extracting Set in expression", &ob, e))?;
171 Ok(Self::Set(s))
172 }
173 "Tuple" => {
174 let t = ob.extract().map_err(|e| extraction_failure("extracting Tuple in expression", &ob, e))?;
175 Ok(Self::Tuple(t))
176 }
177 "Subscript" => {
178 let s = ob.extract().map_err(|e| extraction_failure("extracting Subscript in expression", &ob, e))?;
179 Ok(Self::Subscript(s))
180 }
181 "Starred" => {
182 let s = ob.extract().map_err(|e| extraction_failure("extracting Starred in expression", &ob, e))?;
183 Ok(Self::Starred(s))
184 }
185 "Yield" => {
186 let y = ob.extract().map_err(|e| extraction_failure("extracting Yield in expression", &ob, e))?;
187 Ok(Self::Yield(y))
188 }
189 "YieldFrom" => {
190 let yf = ob.extract().map_err(|e| extraction_failure("extracting YieldFrom in expression", &ob, e))?;
191 Ok(Self::YieldFrom(yf))
192 }
193 "JoinedStr" => {
194 let js = ob.extract().map_err(|e| extraction_failure("extracting JoinedStr in expression", &ob, e))?;
195 Ok(Self::JoinedStr(js))
196 }
197 "FormattedValue" => {
198 let fv = ob.extract().map_err(|e| extraction_failure("extracting FormattedValue in expression", &ob, e))?;
199 Ok(Self::FormattedValue(fv))
200 }
201 _ => {
202 let err_msg = format!(
203 "Unimplemented expression type {}, {}",
204 expr_type,
205 dump(&ob, None)?
206 );
207 Err(pyo3::exceptions::PyValueError::new_err(
208 ob.error_message("<unknown>", err_msg.as_str()),
209 ))
210 }
211 };
212 r
213 }
214}
215
216impl<'a> CodeGen for ExprType {
217 type Context = CodeGenContext;
218 type Options = PythonOptions;
219 type SymbolTable = SymbolTableScopes;
220
221 fn to_rust(
222 self,
223 ctx: Self::Context,
224 options: Self::Options,
225 symbols: Self::SymbolTable,
226 ) -> std::result::Result<TokenStream, Box<dyn std::error::Error>> {
227 match self {
228 ExprType::Attribute(attribute) => attribute.to_rust(ctx, options, symbols),
229 ExprType::Await(func) => func.to_rust(ctx, options, symbols),
230 ExprType::BinOp(binop) => binop.to_rust(ctx, options, symbols),
231 ExprType::BoolOp(boolop) => boolop.to_rust(ctx, options, symbols),
232 ExprType::Call(call) => call.to_rust(ctx, options, symbols),
233 ExprType::Compare(c) => c.to_rust(ctx, options, symbols),
234 ExprType::Constant(c) => c.to_rust(ctx, options, symbols),
235 ExprType::Lambda(l) => l.to_rust(ctx, options, symbols),
236 ExprType::IfExp(i) => i.to_rust(ctx, options, symbols),
237 ExprType::Dict(d) => d.to_rust(ctx, options, symbols),
238 ExprType::Set(s) => s.to_rust(ctx, options, symbols),
239 ExprType::ListComp(lc) => lc.to_rust(ctx, options, symbols),
240 ExprType::DictComp(dc) => dc.to_rust(ctx, options, symbols),
241 ExprType::SetComp(sc) => sc.to_rust(ctx, options, symbols),
242 ExprType::GeneratorExp(ge) => ge.to_rust(ctx, options, symbols),
243 ExprType::Tuple(t) => t.to_rust(ctx, options, symbols),
244 ExprType::Subscript(s) => s.to_rust(ctx, options, symbols),
245 ExprType::Starred(s) => s.to_rust(ctx, options, symbols),
246 ExprType::Yield(y) => y.to_rust(ctx, options, symbols),
247 ExprType::YieldFrom(yf) => yf.to_rust(ctx, options, symbols),
248 ExprType::JoinedStr(js) => js.to_rust(ctx, options, symbols),
249 ExprType::FormattedValue(fv) => fv.to_rust(ctx, options, symbols),
250 ExprType::List(l) => {
251 let mut elements = Vec::new();
252 let mut has_starred = false;
253
254 for li in l {
255 let code = li
256 .clone()
257 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
258
259 if matches!(li, ExprType::Starred(_)) {
261 has_starred = true;
262 let code_str = code.to_string();
263 if code_str.contains("sys :: argv") {
265 elements.push(quote! { __STARRED_ARGV_MARKER__ });
267 } else {
268 elements.push(code);
269 }
270 } else {
271 elements.push(code);
272 }
273 }
274
275 if has_starred {
277 let mut final_elements = Vec::new();
278 let mut has_argv_starred = false;
279
280 for element in elements {
281 let elem_str = element.to_string();
282 if elem_str.contains("__STARRED_ARGV_MARKER__") {
283 has_argv_starred = true;
284 continue; } else {
286 final_elements.push(element);
287 }
288 }
289
290 if has_argv_starred {
292 if final_elements.is_empty() {
293 Ok(quote! {
295 (*sys::argv).clone()
296 })
297 } else {
298 Ok(quote! {
301 {
302 let mut vec = Vec::new();
303 #(vec.push((#final_elements).clone().to_string());)*
304 vec.extend((*sys::argv).iter().cloned());
305 vec
306 }
307 })
308 }
309 } else {
310 Ok(quote! {
312 vec![#(#final_elements),*]
313 })
314 }
315 } else {
316 Ok(quote! {
319 vec![#(#elements),*]
320 })
321 }
322 }
323 ExprType::Name(name) => name.to_rust(ctx, options, symbols),
324 ExprType::NoneType(_) => Ok(quote!(None)),
328 ExprType::UnaryOp(operand) => operand.to_rust(ctx, options, symbols),
329
330 _ => {
331 let error = err_from(ExprTypeNotYetImplemented(self));
332 Err(error.into())
333 }
334 }
335 }
336}
337
338#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
341pub struct Expr {
342 pub value: ExprType,
343 pub ctx: Option<String>,
344 pub lineno: Option<usize>,
345 pub col_offset: Option<usize>,
346 pub end_lineno: Option<usize>,
347 pub end_col_offset: Option<usize>,
348}
349
350impl<'a, 'py> FromPyObject<'a, 'py> for Expr {
351 type Error = pyo3::PyErr;
352 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
353 let err_msg = format!("extracting object value {} in expression", dump(&ob, None)?);
354
355 let ob_value = ob
356 .getattr("value")
357 .map_err(|e| extraction_failure("expression value", &ob, format!("{}: {}", err_msg, e)))?;
358 tracing::debug!("ob_value: {}", dump(&ob_value, None)?);
359
360 let ctx: Option<String> = if let Ok(pyany) = ob_value.getattr("ctx") {
362 pyany.get_type().extract().unwrap_or_default()
363 } else {
364 None
365 };
366
367 let mut r = Self {
368 value: ExprType::Unknown,
369 ctx: ctx,
370 lineno: ob.lineno(),
371 col_offset: ob.col_offset(),
372 end_lineno: ob.end_lineno(),
373 end_col_offset: ob.end_col_offset(),
374 };
375
376 let expr_type = ob_value
377 .get_type()
378 .name()
379 .map_err(|e| extraction_failure("expression type name", &ob, e))?;
380 tracing::debug!(
381 "expression type: {}, value: {}",
382 expr_type,
383 dump(&ob_value, None)?
384 );
385 match expr_type.extract::<String>()?.as_str() {
386 "Attribute" => {
387 let a = ob_value
388 .extract()
389 .map_err(|e| extraction_failure("Attribute expression", &ob_value, e))?;
390 r.value = ExprType::Attribute(a);
391 Ok(r)
392 }
393 "Await" => {
394 let a = ob_value
395 .extract()
396 .map_err(|e| extraction_failure("Await expression", &ob_value, e))?;
397 r.value = ExprType::Await(a);
398 Ok(r)
399 }
400 "BinOp" => {
401 let c = ob_value
402 .extract()
403 .map_err(|e| extraction_failure("BinOp expression", &ob_value, e))?;
404 r.value = ExprType::BinOp(c);
405 Ok(r)
406 }
407 "BoolOp" => {
408 let c = ob_value
409 .extract()
410 .map_err(|e| extraction_failure("BoolOp expression", &ob_value, e))?;
411 r.value = ExprType::BoolOp(c);
412 Ok(r)
413 }
414 "Call" => {
415 let et = ob_value
416 .extract()
417 .map_err(|e| extraction_failure("Call expression", &ob_value, e))?;
418 r.value = ExprType::Call(et);
419 Ok(r)
420 }
421 "Constant" => {
422 let c = ob_value
423 .extract()
424 .map_err(|e| extraction_failure("Constant expression", &ob_value, e))?;
425 r.value = ExprType::Constant(c);
426 Ok(r)
427 }
428 "Compare" => {
429 let c = ob_value
430 .extract()
431 .map_err(|e| extraction_failure("Compare expression", &ob_value, e))?;
432 r.value = ExprType::Compare(c);
433 Ok(r)
434 }
435 "List" => {
436 let elts_attr = ob_value
438 .getattr("elts")
439 .map_err(|e| extraction_failure("list elements", &ob_value, e))?;
440 let elts_vec: Vec<Bound<PyAny>> = elts_attr
441 .extract()
442 .map_err(|e| extraction_failure("list elements", &ob_value, e))?;
443
444 let mut expr_list = Vec::new();
446 for elt in elts_vec {
447 let expr: ExprType = elt
448 .extract()
449 .map_err(|e| extraction_failure("list element", &elt, e))?;
450 expr_list.push(expr);
451 }
452
453 r.value = ExprType::List(expr_list);
454 Ok(r)
455 }
456 "Name" => {
457 let name = ob_value
458 .extract()
459 .map_err(|e| extraction_failure("Name expression", &ob_value, e))?;
460 r.value = ExprType::Name(name);
461 Ok(r)
462 }
463 "UnaryOp" => {
464 let c = ob_value
465 .extract()
466 .map_err(|e| extraction_failure("UnaryOp expression", &ob_value, e))?;
467 r.value = ExprType::UnaryOp(c);
468 Ok(r)
469 }
470 "Lambda" => {
471 let l = ob_value
472 .extract()
473 .map_err(|e| extraction_failure("Lambda expression", &ob_value, e))?;
474 r.value = ExprType::Lambda(l);
475 Ok(r)
476 }
477 "IfExp" => {
478 let i = ob_value
479 .extract()
480 .map_err(|e| extraction_failure("IfExp expression", &ob_value, e))?;
481 r.value = ExprType::IfExp(i);
482 Ok(r)
483 }
484 "Dict" => {
485 let d = ob_value
486 .extract()
487 .map_err(|e| extraction_failure("Dict expression", &ob_value, e))?;
488 r.value = ExprType::Dict(d);
489 Ok(r)
490 }
491 "Set" => {
492 let s = ob_value
493 .extract()
494 .map_err(|e| extraction_failure("Set expression", &ob_value, e))?;
495 r.value = ExprType::Set(s);
496 Ok(r)
497 }
498 "Tuple" => {
499 let t = ob_value
500 .extract()
501 .map_err(|e| extraction_failure("Tuple expression", &ob_value, e))?;
502 r.value = ExprType::Tuple(t);
503 Ok(r)
504 }
505 "Subscript" => {
506 let s = ob_value
507 .extract()
508 .map_err(|e| extraction_failure("Subscript expression", &ob_value, e))?;
509 r.value = ExprType::Subscript(s);
510 Ok(r)
511 }
512 "Yield" => {
513 let y = ob_value
514 .extract()
515 .map_err(|e| extraction_failure("Yield expression", &ob_value, e))?;
516 r.value = ExprType::Yield(y);
517 Ok(r)
518 }
519 "YieldFrom" => {
520 let yf = ob_value
521 .extract()
522 .map_err(|e| extraction_failure("YieldFrom expression", &ob_value, e))?;
523 r.value = ExprType::YieldFrom(yf);
524 Ok(r)
525 }
526 "JoinedStr" => {
527 let js = ob_value
528 .extract()
529 .map_err(|e| extraction_failure("JoinedStr expression", &ob_value, e))?;
530 r.value = ExprType::JoinedStr(js);
531 Ok(r)
532 }
533 "FormattedValue" => {
534 let fv = ob_value
535 .extract()
536 .map_err(|e| extraction_failure("FormattedValue expression", &ob_value, e))?;
537 r.value = ExprType::FormattedValue(fv);
538 Ok(r)
539 }
540 "GeneratorExp" => {
541 let ge = ob_value
542 .extract()
543 .map_err(|e| extraction_failure("GeneratorExp expression", &ob_value, e))?;
544 r.value = ExprType::GeneratorExp(ge);
545 Ok(r)
546 }
547 "NoneType" => {
549 r.value = ExprType::NoneType(Constant(None));
550 Ok(r)
551 }
552 _ => {
553 let err_msg = format!(
554 "Unimplemented expression type {}, {}",
555 expr_type,
556 dump(&ob, None)?
557 );
558 Err(pyo3::exceptions::PyValueError::new_err(
559 ob.error_message("<unknown>", err_msg.as_str()),
560 ))
561 }
562 }
563 }
564}
565
566impl CodeGen for Expr {
567 type Context = CodeGenContext;
568 type Options = PythonOptions;
569 type SymbolTable = SymbolTableScopes;
570
571 fn to_rust(
572 self,
573 ctx: Self::Context,
574 options: Self::Options,
575 symbols: Self::SymbolTable,
576 ) -> std::result::Result<TokenStream, Box<dyn std::error::Error>> {
577 if matches!(self.value, ExprType::NoneType(_)) {
581 return Ok(quote!());
582 }
583 self.value.to_rust(ctx, options, symbols)
584 }
585}
586
587impl Node for Expr {
588 fn lineno(&self) -> Option<usize> {
589 self.lineno
590 }
591
592 fn col_offset(&self) -> Option<usize> {
593 self.col_offset
594 }
595
596 fn end_lineno(&self) -> Option<usize> {
597 self.end_lineno
598 }
599
600 fn end_col_offset(&self) -> Option<usize> {
601 self.end_col_offset
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608
609 #[test]
610 fn check_call_expression() {
611 let expression = crate::parse("test()", "test.py").unwrap();
612 let mut options = PythonOptions::default();
613 options.with_std_python = false;
614 let symbols = SymbolTableScopes::new();
615 let tokens = expression
616 .clone()
617 .to_rust(CodeGenContext::Module("test".to_string()), options, symbols)
618 .unwrap();
619 assert_eq!(
620 tokens.to_string(),
621 "fn __module_init__ () -> Result < () , PyException > { test () ; Ok (()) } \
622 fn main () { if let Err (e) = __module_init__ () { eprintln ! (\"{}\" , e) ; \
623 std :: process :: exit (1) ; } }"
624 );
625 }
626}
627
628pub fn condition_to_rust(
635 expr: &ExprType,
636 ctx: CodeGenContext,
637 options: PythonOptions,
638 symbols: SymbolTableScopes,
639) -> Result<TokenStream, Box<dyn std::error::Error>> {
640 match expr {
641 ExprType::BoolOp(op)
642 if matches!(op.op, crate::BoolOps::And | crate::BoolOps::Or) =>
643 {
644 let mut parts = Vec::new();
645 for value in &op.values {
646 parts.push(condition_to_rust(
647 value,
648 ctx.clone(),
649 options.clone(),
650 symbols.clone(),
651 )?);
652 }
653 Ok(match op.op {
654 crate::BoolOps::And => quote!(#((#parts))&&*),
655 _ => quote!(#((#parts))||*),
656 })
657 }
658 ExprType::UnaryOp(u) if matches!(u.op, crate::Ops::Not) => {
659 let inner = condition_to_rust(&u.operand, ctx, options, symbols)?;
660 Ok(quote!(!(#inner)))
661 }
662 ExprType::Compare(_) => expr.clone().to_rust(ctx, options, symbols),
664 ExprType::Constant(c) if matches!(&c.0, Some(litrs::Literal::Bool(_))) => {
666 expr.clone().to_rust(ctx, options, symbols)
667 }
668 other => {
669 let tokens = other.clone().to_rust(ctx, options, symbols)?;
670 Ok(quote!((#tokens).is_truthy()))
671 }
672 }
673}