journal_sdk/
evaluator.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5#![allow(warnings)]
6include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
7
8use std::fmt::Write;
9use std::collections::HashMap;
10use std::ffi::{CString, CStr};
11use std::num::ParseIntError;
12use std::os::raw::c_char;
13use rand::RngCore;
14use rand::rngs::OsRng;
15use libc::free;
16
17type PrimitiveFunction = unsafe extern "C" fn(*mut s7_scheme, s7_pointer) -> s7_pointer;
18
19#[derive(Clone)]
20pub struct Primitive {
21    code: PrimitiveFunction,
22    name: &'static CStr,
23    description: &'static CStr,
24    args_required: usize,
25    args_optional: usize,
26    args_rest: bool,
27}
28
29impl Primitive {
30    pub fn new(
31        code: PrimitiveFunction,
32        name: &'static CStr,
33        description: &'static CStr,
34        args_required: usize,
35        args_optional: usize,
36        args_rest: bool,
37    ) -> Self {
38        Self {
39            code,
40            name,
41            description,
42            args_required,
43            args_optional,
44            args_rest,
45        }
46    }
47}
48
49pub struct Type {
50    name: &'static CStr,
51    free: PrimitiveFunction,
52    mark: PrimitiveFunction,
53    is_equal: PrimitiveFunction,
54    to_string: PrimitiveFunction,
55}
56
57impl Type {
58    pub fn new(
59        name: &'static CStr,
60        free: PrimitiveFunction,
61        mark: PrimitiveFunction,
62        is_equal: PrimitiveFunction,
63        to_string: PrimitiveFunction,
64    ) -> Self {
65        Self {
66            name,
67            free,
68            mark,
69            is_equal,
70            to_string,
71        }
72    }
73}
74
75pub fn to_str_or_err(expression: &CStr) -> String {
76    match expression.to_str() {
77        Ok(expr) => expr.to_owned(),
78        Err(_) => format!("(error 'encoding-error \"Failed to encode string\")"),
79    }
80}
81
82pub struct Evaluator {
83    pub sc: *mut s7_scheme,
84    primitives: Vec<Primitive>,
85}
86
87impl Evaluator {
88    pub fn new(types: HashMap<i64, Type>, primitives: Vec<Primitive>) -> Self {
89        let mut primitives_ = vec![
90            primitive_hex_string_to_byte_vector(),
91            primitive_byte_vector_to_hex_string(),
92            primitive_expression_to_byte_vector(),
93            primitive_byte_vector_to_expression(),
94            primitive_random_byte_vector(),
95        ];
96
97        primitives_.extend(primitives);
98
99        unsafe {
100            let sc: *mut s7_scheme = s7_init();
101
102            // remove insecure primitives
103            for primitive in REMOVE {
104                s7_define(
105                    sc,
106                    s7_rootlet(sc),
107                    s7_make_symbol(sc, primitive.as_ptr()),
108                    s7_make_symbol(sc, c"*removed*".as_ptr())
109                );
110            }
111
112            // add new types
113            for (&tag_, type_) in types.iter() {
114                let tag = s7_make_c_type(sc, type_.name.as_ptr());
115                assert!(tag == tag_, "Type tag was not properly set");
116                s7_c_type_set_gc_free(sc, tag, Some(type_.free));
117                s7_c_type_set_gc_mark(sc, tag, Some(type_.mark));
118                s7_c_type_set_is_equal(sc, tag, Some(type_.is_equal));
119                s7_c_type_set_to_string(sc, tag, Some(type_.to_string));
120            }
121
122            // add new primitives
123            for primitive in primitives_.iter() {
124                s7_define_function(
125                    sc,
126                    primitive.name.as_ptr(),
127                    Some(primitive.code),
128                    primitive.args_required.try_into().unwrap(),
129                    primitive.args_optional.try_into().unwrap(),
130                    primitive.args_rest,
131                    primitive.description.as_ptr(),
132                );
133            }
134
135            Self { sc, primitives: primitives_ }
136        }
137
138    }
139
140    pub fn evaluate(&self, code: &str) -> String {
141        unsafe {
142            // execute query and return
143            let wrapped = CString::new(format!(
144                "(catch #t (lambda () (eval (read (open-input-string \"{}\")))) (lambda x {}))",
145                code.replace("\\", "\\\\").replace("\"", "\\\""),
146                "`(error ',(car x) ,(apply format (cons #f (cadr x))))",
147            )).unwrap();
148            let s7_obj = s7_eval_c_string(self.sc, wrapped.as_ptr());
149            let output = s7_object_to_c_string(self.sc, s7_obj);
150            let ret = to_str_or_err(CStr::from_ptr(output));
151            free(output as *mut libc::c_void);
152            ret
153        }
154    }
155}
156
157impl Drop for Evaluator {
158    fn drop(&mut self) {
159        unsafe {
160            s7_free(self.sc);
161        }
162    }
163}
164
165fn primitive_expression_to_byte_vector() -> Primitive {
166    unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
167        let arg = s7_car(args);
168
169        let s7_c_str = s7_object_to_c_string(sc, arg);
170        let c_string = CStr::from_ptr(s7_c_str);
171
172        let bv = s7_make_byte_vector(sc, c_string.to_bytes().len() as i64, 1 as i64, std::ptr::null_mut());
173        for (i, b) in c_string.to_bytes().iter().enumerate() { s7_byte_vector_set(bv, i as i64, *b); }
174        free(s7_c_str as *mut libc::c_void);
175        bv
176    }
177
178    Primitive::new(
179        code,
180        c"expression->byte-vector",
181        c"(expression->byte-vector expr) convert a expression string to a byte vector",
182        1, 0, false,
183    )
184}
185
186fn primitive_byte_vector_to_expression() -> Primitive {
187    unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
188        let arg = s7_car(args);
189
190        if !s7_is_byte_vector(arg) {
191            return s7_wrong_type_arg_error(
192                sc, c"byte-vector->expression".as_ptr(), 1, arg,
193                c"a byte-vector".as_ptr())
194        }
195
196        let mut bytes = vec![39];  // quote so that it evaluates correctly
197        for i in 0..s7_vector_length(arg) { bytes.push(s7_byte_vector_ref(arg, i)) }
198        bytes.push(0);
199
200        let c_string = CString::from_vec_with_nul(bytes).unwrap();
201        s7_eval_c_string(sc, c_string.as_ptr())
202    }
203
204    Primitive::new(
205        code,
206        c"byte-vector->expression",
207        c"(byte-vector->expression bv) convert a byte vector to an expression",
208        1, 0, false,
209    )
210}
211
212fn primitive_hex_string_to_byte_vector() -> Primitive {
213    unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
214        let arg = s7_car(args);
215
216        if !s7_is_string(arg) {
217            return s7_wrong_type_arg_error(
218                sc, c"hex-string->byte-vector".as_ptr(), 1, arg,
219                c"a hex string".as_ptr())
220        }
221
222        let s7_c_str = s7_object_to_c_string(sc, arg);
223        let hex_string = CStr::from_ptr(s7_c_str).to_str().unwrap();
224
225        let result: Result<Vec<u8>, ParseIntError> = (1..hex_string.len()-1)
226            .step_by(2)
227            .map(|i| u8::from_str_radix(&hex_string[i..i + 2], 16))
228            .collect();
229
230        free(s7_c_str as *mut libc::c_void);
231
232        match result {
233            Ok(result) => {
234                let bv = s7_make_byte_vector(sc, result.len() as i64, 1 as i64, std::ptr::null_mut());
235                for i in 0..result.len() { s7_byte_vector_set(bv, i as i64, result[i]); }
236                bv
237            }
238            _ => {
239                s7_wrong_type_arg_error(
240                    sc, c"hex-string->byte-vector".as_ptr(), 1, arg,
241                    c"a hex string".as_ptr())
242            }
243        }
244    }
245
246    Primitive::new(
247        code,
248        c"hex-string->byte-vector",
249        c"(hex-string->byte-vector str) convert a hex string to a byte vector",
250        1, 0, false,
251    )
252}
253
254fn primitive_byte_vector_to_hex_string() -> Primitive {
255    unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
256        let arg = s7_car(args);
257
258        if !s7_is_byte_vector(arg) {
259            return s7_wrong_type_arg_error(
260                sc, c"byte-vector->hex-string".as_ptr(), 1, arg,
261                c"a byte-vector".as_ptr())
262        }
263
264        let mut bytes = vec![0 as u8; s7_vector_length(arg) as usize];
265        for i in 0..bytes.len() as usize { bytes[i] = s7_byte_vector_ref(arg, i as i64); }
266
267        let mut string = String::with_capacity(bytes.len() * 2);
268        for b in bytes { write!(&mut string,"{:02x}", b).unwrap(); }
269
270        // todo: this might cause a pointer issue
271        let c_string = CString::new(string).unwrap();
272        s7_object_to_string(sc, s7_make_string(sc, c_string.as_ptr()), false)
273    }
274
275    Primitive::new(
276        code,
277        c"byte-vector->hex-string",
278        c"(byte-vector->hex-string bv) convert a byte vector to a hex string",
279        1, 0, false,
280    )
281}
282
283fn primitive_random_byte_vector() -> Primitive {
284    unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
285        let arg = s7_car(args);
286
287        if !s7_is_integer(arg) || s7_integer(arg) < 0 {
288            return s7_wrong_type_arg_error(
289                sc, c"random-byte-vector".as_ptr(), 1, arg,
290                c"a non-negative integer".as_ptr())
291        }
292
293        let length = s7_integer(arg);
294        let mut rng = OsRng;
295        let mut bytes = vec![0u8; length.try_into().unwrap()];
296        rng.fill_bytes(&mut bytes);
297
298        let bv = s7_make_byte_vector(sc, length as i64, 1, std::ptr::null_mut());
299        for i in 0..length as usize { s7_byte_vector_set(bv, i as i64, bytes[i]); }
300        bv
301    }
302
303    Primitive::new(
304        code,
305        c"random-byte-vector",
306        c"(random-byte-vector length) generate a securely random byte vector of the provided length",
307        1, 0, false,
308    )
309}
310
311static REMOVE: [&'static CStr; 84] = [
312    c"*autoload*",
313    c"*autoload-hook*",
314    c"*cload-directory*",
315    c"*features*",
316    c"*function*",
317    c"*libraries*",
318    c"*load-hook*",
319    c"*load-path*",
320    c"*stderr*",
321    c"*stdin*",
322    c"*stdout*",
323    c"abort",
324    c"autoload",
325    c"c-object-type",
326    c"c-object?",
327    c"c-pointer",
328    c"c-pointer->list",
329    c"c-pointer-info",
330    c"c-pointer-type",
331    c"c-pointer-weak1",
332    c"c-pointer-weak2",
333    c"c-pointer?",
334    c"call-with-current-continuation",
335    c"call-with-exit",
336    c"call-with-input-file",
337    c"call-with-input-file",
338    c"call-with-input-string",
339    c"call-with-output-file",
340    c"call-with-output-string",
341    c"call/cc",
342    c"close-input-port",
343    c"close-output-port",
344    c"continuation?",
345    c"current-error-port",
346    c"current-input-port",
347    c"current-output-port",
348    c"dilambda",
349    c"dilambda?",
350    c"dynamic-unwind",
351    c"dynamic-wind",
352    c"emergency-exit",
353    c"exit",
354    c"flush-output-port",
355    c"gc",
356    c"get-output-string",
357    c"goto?",
358    c"hook-functions",
359    c"input-port?",
360    c"load",
361    c"make-hook",
362    c"open-input-file",
363    c"open-input-function",
364    c"open-output-file",
365    c"open-output-function",
366    c"open-output-string",
367    c"output-port?",
368    c"owlet",
369    c"pair-filename",
370    c"pair-line-number",
371    c"peek-char",
372    c"port-closed?",
373    c"port-file",
374    c"port-filename",
375    c"port-line-number",
376    c"port-position",
377    c"profile-in",
378    c"random",
379    c"read-char",
380    c"read-string",
381    c"read-byte",
382    c"read-line",
383    c"require",
384    c"s7-optimize",
385    c"set-current-error-port",
386    c"stacktrace",
387    c"unlet",
388    c"with-baffle",
389    c"with-input-from-file",
390    c"with-output-to-file",
391    c"with-output-to-string",
392    c"write",
393    c"write-byte",
394    c"write-char",
395    c"write-string",
396];