summaryrefslogtreecommitdiff
path: root/src/guile/guile.rs
blob: a459a64efd921739fc437ee474322ecfa0d90b17 (plain) (blame)
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
134
135
136
137
138
139
140
141
142
143
144
145
use crate::macro_processor::macro_processor::MacroProcessor;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void};

use std::sync::mpsc;
use std::thread;

macro_rules! dprint {
    ($($x:tt)*) => {
        #[cfg(debug_assertions)]
        print!($($x)*)
    }
}

#[link(name = "guile-3.0")]
extern "C" {
    fn scm_init_guile();
    fn scm_c_eval_string(expr: *const c_char) -> *mut c_void;
    fn scm_from_utf8_string(expr: *const c_char) -> *mut c_void;
    fn scm_to_locale_string(scm_obj: *mut c_void) -> *const c_char;
    fn scm_is_string(scm_obj: *mut c_void) -> c_int;
    fn scm_object_to_string(scm_obj: *mut c_void, printer: *mut c_void) -> *mut c_void;
    fn scm_to_pointer(scm_obj: *mut c_void) -> *mut c_void;
    fn scm_c_define_gsubr(
        name: *const c_char,
        req: c_int,
        opt: c_int,
        rst: c_int,
        func: extern "C" fn(scm_obj: *mut c_void) -> *mut c_void,
    );
    fn scm_c_define(name: *const c_char, value: *mut c_void);
    fn scm_from_pointer(value: *mut c_void) -> *mut c_void;
    //fn scm_with_guile(func: extern "C" fn(data: *mut c_void) -> *mut c_void, data: *mut c_void) -> *mut c_void;
    //fn scm_is_number(scm_obj: *mut c_void) -> c_int;
    //fn scm_without_guile(func: extern "C" fn(data: *mut c_void) -> *mut c_void, data: *mut c_void) -> *mut c_void;
    //fn scm_c_write(scm_obj: *mut c_void, port: *mut c_void);
    //fn scm_get_output_string(port: *mut c_void) -> *mut c_void;
    //fn scm_open_output_string() -> *mut c_void;
}

#[link(name = "guiledefs")]
extern "C" {
    pub static scm_undefined: *mut c_void;
    pub static scm_unspecified: *mut c_void;
    fn defs_scm_is_eq(x: *mut c_void, y: *mut c_void) -> c_int;
    fn defs_scm_define_string(name: *const c_char, value: *const c_char);
}

/// Convert a scm object into a string using Guile-builtins
fn string_from_scm(scm_obj: *mut c_void) -> Result<String, ()> {
    unsafe {
        if scm_obj.is_null() {
            return Err(());
        }
        if defs_scm_is_eq(scm_obj, scm_unspecified) != 0 {
            return Ok(String::new());
        } else if scm_is_string(scm_obj) != 0 {
            let res_str = CStr::from_ptr(scm_to_locale_string(scm_obj))
                .to_string_lossy()
                .into_owned();
            return Ok(res_str);
        } else {
            let res = scm_object_to_string(scm_obj, scm_undefined);
            if res.is_null() {
                return Err(());
            }
            let res_str = CStr::from_ptr(scm_to_locale_string(res))
                .to_string_lossy()
                .into_owned();
            return Ok(res_str);
        }
    }
}

extern "C" fn scm_smp_macro(arg: *mut c_void) -> *mut c_void {
    let arg_str;
    unsafe {
        arg_str = CStr::from_ptr(scm_to_locale_string(arg))
            .to_string_lossy()
            .into_owned();
    }
    dprint!("ARG {:#?}\n", arg_str);
    let c_smp_state_ptr = CString::new("smp_state_ptr").expect("CString::new() failed");
    unsafe {
        let smp_state_scm = scm_c_eval_string(c_smp_state_ptr.as_ptr());
        let smp_state_ptr = scm_to_pointer(smp_state_scm);
        let smp: &mut MacroProcessor = &mut *(smp_state_ptr as *mut MacroProcessor);
        if let Some(macro_value) = smp.macros.get(&arg_str) {
            let r = CString::new(macro_value.to_string()).expect("CString::new() failed");
            scm_from_utf8_string(r.as_ptr())
        } else {
            let r = CString::new("Macro not found").expect("CString::new() failed");
            scm_from_utf8_string(r.as_ptr())
        }
    }
}

#[derive(Debug)]
pub struct Guile {}

impl Guile {
    pub fn new() -> Self {
        unsafe {
            scm_init_guile();
        }
        let guile = Guile {};
        let func_name = CString::new("smp_get").unwrap();
        unsafe {
            scm_c_define_gsubr(func_name.as_ptr(), 1, 0, 0, scm_smp_macro);
        }
        guile
    }

    pub fn evaluate_expression(&self, expr: &str) -> Result<String, ()> {
        dprint!("(eval \"{}\")\n", expr);
        unsafe {
            let c_expr = CString::new(expr).map_err(|_| ())?;
            let result = scm_c_eval_string(c_expr.as_ptr());
            string_from_scm(result)
        }
    }

    pub fn define_string(&self, name: &str, value: &str) {
        let c_name = CString::new(name).expect("CString::new failed");
        let c_value = CString::new(value).expect("CString::new failed");
        unsafe {
            defs_scm_define_string(c_name.as_ptr(), c_value.as_ptr());
        }
    }

    pub fn define(&self, name: &str, value: *mut c_void) {
        let c_name = CString::new(name).expect("CString::new failed");
        unsafe {
            scm_c_define(c_name.as_ptr(), scm_from_pointer(value));
        }
    }
}

impl Drop for Guile {
    fn drop(&mut self) {
        //if let Err(e) = self.evaluate_expression("(variable-unset! \"smp_state_ptr\")") {
        //    panic!("Error while exiting {:#?}", e);
        //}
    }
}