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(),
			}
		}
	}
}