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