1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#![recursion_limit="128"] extern crate proc_macro; use proc_macro::TokenStream as TokenStream1; use proc_macro2::{Delimiter, Spacing, TokenStream, TokenTree}; use quote::quote; use std::fmt::Write; use std::collections::BTreeSet; #[proc_macro] pub fn python(input: TokenStream1) -> TokenStream1 { let mut x = EmbedPython { python: String::new(), variables: TokenStream::new(), variable_names: BTreeSet::new(), line: 0, indent: None, }; x.add(TokenStream::from(input)); let EmbedPython { python, variables, .. } = x; let q = quote! { { let _python_lock = ::inline_python::pyo3::Python::acquire_gil(); let mut _python_variables = ::inline_python::pyo3::types::PyDict::new(_python_lock.python()); #variables match _python_lock.python().run(#python, None, Some(_python_variables)) { Ok(_) => (), Err(e) => { e.print(_python_lock.python()); panic!("Python code failed"); } } } }; q.into() } struct EmbedPython { python: String, variables: TokenStream, variable_names: BTreeSet<String>, line: usize, indent: Option<usize>, } impl EmbedPython { fn add(&mut self, input: TokenStream) { let mut tokens = input.into_iter(); while let Some(token) = tokens.next() { let loc = token.span().start(); if loc.line != self.line { self.python.push('\n'); let indent = *self.indent.get_or_insert(loc.column); for _ in 0..(loc.column.saturating_sub(indent)) { self.python.push(' '); } self.line = loc.line; } match &token { TokenTree::Group(x) => { let (start, end) = match x.delimiter() { Delimiter::Parenthesis => ('(', ')'), Delimiter::Brace => ('{', '}'), Delimiter::Bracket => ('[', ']'), Delimiter::None => (' ', ' '), }; self.python.push(start); self.add(x.stream()); self.python.push(end); } TokenTree::Punct(x) => { if x.as_char() == '\'' && x.spacing() == Spacing::Joint { let name = if let Some(TokenTree::Ident(name)) = tokens.next() { name } else { panic!() }; let pyname = format!("_rust_{}", name); if self.variable_names.insert(name.to_string()) { self.variables.extend(quote! { _python_variables.set_item(#pyname, #name) .expect("Unable to convert variable to Python"); }); } self.python.push_str(&pyname); self.python.push(' '); } else { self.python.push(x.as_char()); if x.spacing() == Spacing::Alone { self.python.push(' '); } } } TokenTree::Ident(x) => write!(&mut self.python, "{} ", x).unwrap(), TokenTree::Literal(x) => write!(&mut self.python, "{} ", x).unwrap(), } } } }