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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
#![feature(proc_macro_span)]
use std::path::Path;
use std::fs;
use proc_macro::{ Span, TokenStream };
use proc_macro_error::{
proc_macro_error,
abort,
//abort_call_site,
//ResultExt,
//OptionExt,
};
use quote::{quote, format_ident};
use python_ast::{parse, PythonOptions, CodeGen, CodeGenContext};
fn module_options(input: TokenStream, options: PythonOptions) -> TokenStream {
let mod_name = input.to_string();
let mod_path = match Span::mixed_site().parent() {
Some(p) => p,
_ => abort!("unable to determine parent of module for {}", mod_name)
}.source_file().path();
let mod_name_dir = format!("src/{}/__init__.py", &mod_name);
let mod_name_file = format!("src/{}.py", &mod_name);
let python_str_result = if Path::new(&mod_name_dir).exists() {
fs::read_to_string(mod_name_dir)
} else if Path::new(&mod_name_file).exists() {
fs::read_to_string(mod_name_file)
} else {
abort!(mod_name, "Module not found {} in {:?}", mod_name, mod_path)
};
let python_str = match python_str_result {
Ok(s) => s,
Err(e) => abort!("1: {:?}", e)
};
// Build the parse tree
let py_mod = match parse(&python_str, &mod_name) {
Ok(p) => p,
Err(e) => abort!("parse error: {:?}", e)
};
//let options = PythonOptions::default();
// Convert the parse tree to Rust a token stream.
let output = match py_mod.to_rust(CodeGenContext::Module, options) {
Ok(o) => o,
Err(e) => abort!("transpiler error: {:?}", e)
};
let new_mod_name = format_ident!("{}", mod_name);
let result = quote!(mod #new_mod_name {
#output
});
println!("result: {}", result);
result.into()
}
/// Macro taking two parameters. The first is the name of the module, the second is the path to load it from.
///
/// ```Rust
/// use std::module_path;
///
/// python_module!(py_mod);
/// ```
#[proc_macro]
#[proc_macro_error]
pub fn python_module(input: TokenStream) -> TokenStream {
let options = PythonOptions::default();
module_options(input, options)
}
#[proc_macro]
#[proc_macro_error]
/// Loads a python module, but does not include the stdpython libraries.
pub fn python_module_nostd(input: TokenStream) -> TokenStream {
let mut options = PythonOptions::default();
options.with_std_python = false;
module_options(input, options)
}
/*
/// Macro which takes a string input and compiles it into Rust code
/// inline with the surrounding Rust code.
///
/// ```Rust
/// use std::module_path;
///
/// python_inline_string!(current_module_name, python_string);
/// ```
#[proc_macro]
#[proc_macro_error]
pub fn python_inline_string(input: TokenStream) -> TokenStream {
let mut tokens = input.into_iter();
let mod_name = format_ident!("{}", tokens.next().unwrap().to_string());
for token in tokens {
let py_string = token.to_string();
println!("{} tokens: {:?}, {}", mod_name, token, py_string);
}
/*
let python_str = input.to_string();
// Build the parse tree
let py_mod = match parse(&python_str, "_") {
Ok(p) => p,
Err(e) => abort!("parse error: {:?}", e)
};
let mut ctx = PythonContext::default();
// Convert the parse tree to Rust a token stream.
let output = match py_mod.to_rust(&mut ctx) {
Ok(o) => o,
Err(e) => abort!("transpiler error: {:?}", e)
};
let result = quote!(
#output
);
println!("result: {}", result);
result.into()*/
quote!("").into()
}
*/