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
#![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};

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