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 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 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 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 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]; 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 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];