1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(warnings)]
5include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
6
7use libc::free;
8use log::info;
9use rand::RngCore;
10use rand::rngs::OsRng;
11use serde_json::{Map, Value};
12use std::collections::HashMap;
13use std::ffi::{CStr, CString};
14use std::fmt::Write;
15use std::num::ParseIntError;
16use std::os::raw::c_char;
17use std::time::{SystemTime, UNIX_EPOCH};
18
19type PrimitiveFunction = unsafe extern "C" fn(*mut s7_scheme, s7_pointer) -> s7_pointer;
20
21#[derive(Clone)]
22pub struct Primitive {
23 code: PrimitiveFunction,
24 name: &'static CStr,
25 description: &'static CStr,
26 args_required: usize,
27 args_optional: usize,
28 args_rest: bool,
29}
30
31impl Primitive {
32 pub fn new(
33 code: PrimitiveFunction,
34 name: &'static CStr,
35 description: &'static CStr,
36 args_required: usize,
37 args_optional: usize,
38 args_rest: bool,
39 ) -> Self {
40 Self {
41 code,
42 name,
43 description,
44 args_required,
45 args_optional,
46 args_rest,
47 }
48 }
49}
50
51pub struct Type {
52 name: &'static CStr,
53 free: PrimitiveFunction,
54 mark: PrimitiveFunction,
55 is_equal: PrimitiveFunction,
56 to_string: PrimitiveFunction,
57}
58
59impl Type {
60 pub fn new(
61 name: &'static CStr,
62 free: PrimitiveFunction,
63 mark: PrimitiveFunction,
64 is_equal: PrimitiveFunction,
65 to_string: PrimitiveFunction,
66 ) -> Self {
67 Self {
68 name,
69 free,
70 mark,
71 is_equal,
72 to_string,
73 }
74 }
75}
76
77pub fn obj2str(sc: *mut s7_scheme, obj: *mut s7_cell) -> String {
78 unsafe {
79 let expr = s7_string(s7_object_to_string(sc, obj, false));
80 let cstr = CStr::from_ptr(expr);
81 let result = match cstr.to_str() {
82 Ok(rust_str) => match s7_is_string(obj) {
83 true => format!("\"{}\"", rust_str),
84 false => format!("{}", rust_str.to_owned()),
85 },
86 Err(_) => format!("(error 'encoding-error \"Failed to encode string\")"),
87 };
88 result
89 }
90}
91
92pub fn lisp2json(expression: &str) -> Result<Value, String> {
93 let mut owned_expr = None;
112 let expr = {
113 let trimmed = expression.trim_start();
114 if let Some(rest) = trimmed.strip_prefix('\'') {
115 let rest = rest.trim_start();
116 if rest.is_empty() {
117 return Err("Empty quoted expression".to_string());
118 }
119 let mut wrapped = String::from("(quote ");
120 wrapped.push_str(rest);
121 wrapped.push(')');
122 owned_expr = Some(wrapped);
123 owned_expr.as_deref().expect("quote wrapper missing")
124 } else {
125 expression
126 }
127 };
128
129 unsafe {
130 let sc: *mut s7_scheme = s7_init();
131
132 let c_expr = CString::new(expr).unwrap_or_else(|_| CString::new("()").unwrap());
134 let input_port = s7_open_input_string(sc, c_expr.as_ptr());
135 let s7_obj = s7_read(sc, input_port);
136 s7_close_input_port(sc, input_port);
137
138 let result = s7_obj_to_json(sc, s7_obj);
139 s7_free(sc);
140 result
141 }
142}
143
144pub fn json2lisp(expression: &Value) -> Result<String, String> {
145 unsafe {
146 let sc: *mut s7_scheme = s7_init();
147 match json_to_s7_obj(sc, &expression) {
148 Ok(s7_obj) => {
149 let result = obj2str(sc, s7_obj);
150 s7_free(sc);
151 Ok(result)
152 }
153 Err(err) => {
154 s7_free(sc);
155 Err(err)
156 }
157 }
158 }
159}
160
161pub struct Evaluator {
162 pub sc: *mut s7_scheme,
163 primitives: Vec<Primitive>,
164}
165
166impl Evaluator {
167 pub fn new(types: HashMap<i64, Type>, primitives: Vec<Primitive>) -> Self {
168 let mut primitives_ = vec![
169 primitive_hex_string_to_byte_vector(),
170 primitive_byte_vector_to_hex_string(),
171 primitive_expression_to_byte_vector(),
172 primitive_byte_vector_to_expression(),
173 primitive_random_byte_vector(),
174 primitive_time_unix(),
175 primitive_print(),
176 ];
177
178 primitives_.extend(primitives);
179
180 unsafe {
181 let sc: *mut s7_scheme = s7_init();
182
183 for primitive in REMOVE {
185 s7_define(
186 sc,
187 s7_rootlet(sc),
188 s7_make_symbol(sc, primitive.as_ptr()),
189 s7_make_symbol(sc, c"*removed*".as_ptr()),
190 );
191 }
192
193 for (&tag_, type_) in types.iter() {
195 let tag = s7_make_c_type(sc, type_.name.as_ptr());
196 assert!(tag == tag_, "Type tag was not properly set");
197 s7_c_type_set_gc_free(sc, tag, Some(type_.free));
198 s7_c_type_set_gc_mark(sc, tag, Some(type_.mark));
199 s7_c_type_set_is_equal(sc, tag, Some(type_.is_equal));
200 s7_c_type_set_to_string(sc, tag, Some(type_.to_string));
201 }
202
203 for primitive in primitives_.iter() {
205 s7_define_function(
206 sc,
207 primitive.name.as_ptr(),
208 Some(primitive.code),
209 primitive
210 .args_required
211 .try_into()
212 .expect("args_required conversion failed"),
213 primitive
214 .args_optional
215 .try_into()
216 .expect("args_optional conversion failed"),
217 primitive.args_rest,
218 primitive.description.as_ptr(),
219 );
220 }
221
222 Self {
223 sc,
224 primitives: primitives_,
225 }
226 }
227 }
228
229 pub fn evaluate(&self, code: &str) -> String {
230 unsafe {
231 unsafe {
232 let wrapped = CString::new(format!(
234 "(catch #t (lambda () (eval (read (open-input-string \"{}\")))) (lambda x {}))",
235 code.replace("\\", "\\\\").replace("\"", "\\\""),
236 "`(error ',(car x) ,(apply format (cons #f (cadr x))))",
237 ))
238 .expect("failed to create CString for evaluation");
239 let s7_obj = s7_eval_c_string(self.sc, wrapped.as_ptr());
240 obj2str(self.sc, s7_obj)
241 }
242 }
243 }
244}
245
246impl Drop for Evaluator {
247 fn drop(&mut self) {
248 unsafe {
249 s7_free(self.sc);
250 }
251 }
252}
253
254fn primitive_expression_to_byte_vector() -> Primitive {
255 unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
256 let arg = s7_car(args);
257
258 let bytes = obj2str(sc, arg).into_bytes();
261
262 let bv = s7_make_byte_vector(sc, bytes.len() as i64, 1 as i64, std::ptr::null_mut());
263 for (i, b) in bytes.iter().enumerate() {
264 s7_byte_vector_set(bv, i as i64, *b);
265 }
266 bv
267 }
268
269 Primitive::new(
270 code,
271 c"expression->byte-vector",
272 c"(expression->byte-vector expr) convert a expression string to a byte vector",
273 1,
274 0,
275 false,
276 )
277}
278
279fn primitive_byte_vector_to_expression() -> Primitive {
280 unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
281 let arg = s7_car(args);
282
283 if !s7_is_byte_vector(arg) {
284 return s7_wrong_type_arg_error(
285 sc,
286 c"byte-vector->expression".as_ptr(),
287 1,
288 arg,
289 c"a byte-vector".as_ptr(),
290 );
291 }
292
293 let mut bytes = vec![39]; for i in 0..s7_vector_length(arg) {
295 bytes.push(s7_byte_vector_ref(arg, i))
296 }
297 bytes.push(0);
298
299 match CString::from_vec_with_nul(bytes) {
300 Ok(c_string) => s7_eval_c_string(sc, c_string.as_ptr()),
301 Err(_) => s7_error(
302 sc,
303 s7_make_symbol(sc, c"encoding-error".as_ptr()),
304 s7_list(
305 sc,
306 1,
307 s7_make_string(sc, c"Byte vector string is malformed".as_ptr()),
308 ),
309 ),
310 }
311 }
312
313 Primitive::new(
314 code,
315 c"byte-vector->expression",
316 c"(byte-vector->expression bv) convert a byte vector to an expression",
317 1,
318 0,
319 false,
320 )
321}
322
323fn primitive_hex_string_to_byte_vector() -> Primitive {
324 unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
325 let arg = s7_car(args);
326
327 if !s7_is_string(arg) {
328 return s7_wrong_type_arg_error(
329 sc,
330 c"hex-string->byte-vector".as_ptr(),
331 1,
332 arg,
333 c"a hex string".as_ptr(),
334 );
335 }
336
337 let s7_c_str = s7_string(s7_object_to_string(sc, arg, false));
338 let hex_string = CStr::from_ptr(s7_c_str)
339 .to_str()
340 .expect("Failed to convert C string to hex string");
341
342 let result: Result<Vec<u8>, ParseIntError> = (0..hex_string.len())
343 .step_by(2)
344 .map(|i| u8::from_str_radix(&hex_string[i..i + 2], 16))
345 .collect();
346
347 match result {
348 Ok(result) => {
349 let bv =
350 s7_make_byte_vector(sc, result.len() as i64, 1 as i64, std::ptr::null_mut());
351 for i in 0..result.len() {
352 s7_byte_vector_set(bv, i as i64, result[i]);
353 }
354 bv
355 }
356 _ => s7_wrong_type_arg_error(
357 sc,
358 c"hex-string->byte-vector".as_ptr(),
359 1,
360 arg,
361 c"a hex string".as_ptr(),
362 ),
363 }
364 }
365
366 Primitive::new(
367 code,
368 c"hex-string->byte-vector",
369 c"(hex-string->byte-vector str) convert a hex string to a byte vector",
370 1,
371 0,
372 false,
373 )
374}
375
376fn primitive_byte_vector_to_hex_string() -> Primitive {
377 unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
378 let arg = s7_car(args);
379
380 if !s7_is_byte_vector(arg) {
381 return s7_wrong_type_arg_error(
382 sc,
383 c"byte-vector->hex-string".as_ptr(),
384 1,
385 arg,
386 c"a byte-vector".as_ptr(),
387 );
388 }
389
390 let mut bytes = vec![0 as u8; s7_vector_length(arg) as usize];
391 for i in 0..bytes.len() as usize {
392 bytes[i] = s7_byte_vector_ref(arg, i as i64);
393 }
394
395 let mut string = String::with_capacity(bytes.len() * 2);
396 for b in bytes {
397 write!(&mut string, "{:02x}", b).expect("Failed to write byte to hex string");
398 }
399
400 let c_string = CString::new(string).expect("Failed to create C string from hex string");
402 s7_object_to_string(sc, s7_make_string(sc, c_string.as_ptr()), false)
403 }
404
405 Primitive::new(
406 code,
407 c"byte-vector->hex-string",
408 c"(byte-vector->hex-string bv) convert a byte vector to a hex string",
409 1,
410 0,
411 false,
412 )
413}
414
415fn primitive_random_byte_vector() -> Primitive {
416 unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
417 let arg = s7_car(args);
418
419 if !s7_is_integer(arg) || s7_integer(arg) < 0 {
420 return s7_wrong_type_arg_error(
421 sc,
422 c"random-byte-vector".as_ptr(),
423 1,
424 arg,
425 c"a non-negative integer".as_ptr(),
426 );
427 }
428
429 let length = s7_integer(arg);
430 let mut rng = OsRng;
431 let mut bytes = vec![
432 0u8;
433 length
434 .try_into()
435 .expect("Length exceeds system memory limits")
436 ];
437 rng.fill_bytes(&mut bytes);
438
439 let bv = s7_make_byte_vector(sc, length as i64, 1, std::ptr::null_mut());
440 for i in 0..length as usize {
441 s7_byte_vector_set(bv, i as i64, bytes[i]);
442 }
443 bv
444 }
445
446 Primitive::new(
447 code,
448 c"random-byte-vector",
449 c"(random-byte-vector length) generate a securely random byte vector of the provided length",
450 1, 0, false,
451 )
452}
453
454fn primitive_time_unix() -> Primitive {
455 unsafe extern "C" fn code(sc: *mut s7_scheme, _args: s7_pointer) -> s7_pointer {
456 match SystemTime::now().duration_since(UNIX_EPOCH) {
457 Ok(duration) => s7_make_real(sc, duration.as_secs_f64()),
458 Err(_) => s7_error(
459 sc,
460 s7_make_symbol(sc, c"time-error".as_ptr()),
461 s7_list(
462 sc,
463 1,
464 s7_make_string(sc, c"Failed to get system time".as_ptr()),
465 ),
466 ),
467 }
468 }
469
470 Primitive::new(
471 code,
472 c"time-unix",
473 c"(time-unix) returns current Unix time in seconds",
474 0,
475 0,
476 false,
477 )
478}
479
480fn primitive_print() -> Primitive {
481 unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
482 let mut result = String::new();
483 let mut current_arg = args;
484
485 while !s7_is_null(sc, current_arg) {
486 let arg = s7_car(current_arg);
487 let str_rep = obj2str(sc, arg);
488
489 if !result.is_empty() {
490 result.push(' ');
491 }
492 result.push_str(&str_rep);
493 current_arg = s7_cdr(current_arg);
494 }
495
496 println!("{}", result);
497
498 if s7_is_null(sc, args) {
499 s7_unspecified(sc)
500 } else {
501 let mut last_arg = args;
502 while !s7_is_null(sc, s7_cdr(last_arg)) {
503 last_arg = s7_cdr(last_arg);
504 }
505 s7_car(last_arg)
506 }
507 }
508
509 Primitive::new(
510 code,
511 c"print",
512 c"(print obj ...) print objects to the console and returns the last object",
513 0,
514 0,
515 true,
516 )
517}
518
519unsafe fn s7_obj_to_json(sc: *mut s7_scheme, obj: s7_pointer) -> Result<Value, String> {
520 unsafe {
521 if s7_is_null(sc, obj) {
522 Ok(Value::Null)
523 } else if s7_is_boolean(obj) {
524 Ok(Value::Bool(s7_boolean(sc, obj)))
525 } else if s7_is_integer(obj) {
526 Ok(Value::Number(serde_json::Number::from(s7_integer(obj))))
527 } else if s7_is_real(obj) {
528 if let Some(num) = serde_json::Number::from_f64(s7_real(obj)) {
529 Ok(Value::Number(num))
530 } else {
531 Err("Invalid floating point number - cannot convert to JSON".to_string())
532 }
533 } else if s7_is_string(obj) {
534 let rust_str = obj2str(sc, obj);
537
538 let mut special_type = Map::new();
540 special_type.insert(
541 "*type/string*".to_string(),
542 Value::String(String::from(&rust_str[1..(rust_str.len() - 1)])),
543 );
544 Ok(Value::Object(special_type))
545 } else if s7_is_symbol(obj) {
546 let c_str = s7_symbol_name(obj);
547 let rust_str = CStr::from_ptr(c_str).to_string_lossy();
548 Ok(Value::String(rust_str.to_string()))
549 } else if s7_is_pair(obj) {
550 let car = s7_car(obj);
552 if s7_is_syntax(car) {
553 let car_str = obj2str(sc, car);
554 if car_str == "#_quote" {
555 let cdr = s7_cdr(obj);
556 if s7_is_pair(cdr) && s7_is_null(sc, s7_cdr(cdr)) {
557 let quoted_expr = s7_car(cdr);
558 let mut special_type = Map::new();
559 special_type.insert(
560 "*type/quoted*".to_string(),
561 s7_obj_to_json(sc, quoted_expr)?,
562 );
563 return Ok(Value::Object(special_type));
564 }
565 }
566 }
567 if s7_is_symbol(car) {
568 let symbol_name_ptr = s7_symbol_name(car);
569 if !symbol_name_ptr.is_null() {
570 let symbol_name = CStr::from_ptr(symbol_name_ptr).to_string_lossy();
571
572 if symbol_name == "quote" {
573 let cdr = s7_cdr(obj);
575 if s7_is_pair(cdr) && s7_is_null(sc, s7_cdr(cdr)) {
576 let quoted_expr = s7_car(cdr);
577 let mut special_type = Map::new();
578 special_type.insert(
579 "*type/quoted*".to_string(),
580 s7_obj_to_json(sc, quoted_expr)?,
581 );
582 return Ok(Value::Object(special_type));
583 }
584 }
585 }
586 }
587
588 if is_proper_assoc_list(sc, obj) {
590 let mut map = Map::new();
591 let mut current = obj;
592
593 while !s7_is_null(sc, current) {
594 let pair = s7_car(current);
595 if s7_is_pair(pair) {
596 let key_obj = s7_car(pair);
597 let cdr_pair = s7_cdr(pair);
598
599 if s7_is_pair(cdr_pair) && s7_is_null(sc, s7_cdr(cdr_pair)) {
601 let value_obj = s7_car(cdr_pair);
602
603 if s7_is_symbol(key_obj) {
604 let key_c_str = s7_symbol_name(key_obj);
605 let key = CStr::from_ptr(key_c_str).to_string_lossy().to_string();
606 let value = s7_obj_to_json(sc, value_obj)?;
607 map.insert(key, value);
608 }
609 }
610 }
611 current = s7_cdr(current);
612 }
613 Ok(Value::Object(map))
614 } else if s7_is_pair(obj) && !s7_is_pair(s7_cdr(obj)) && !s7_is_null(sc, s7_cdr(obj)) {
615 let mut special_type = Map::new();
617 let mut pair_array = Vec::new();
618 pair_array.push(s7_obj_to_json(sc, s7_car(obj))?);
619 pair_array.push(s7_obj_to_json(sc, s7_cdr(obj))?);
620 special_type.insert("*type/pair*".to_string(), Value::Array(pair_array));
621 Ok(Value::Object(special_type))
622 } else {
623 let mut array = Vec::new();
625 let mut current = obj;
626
627 while !s7_is_null(sc, current) {
628 array.push(s7_obj_to_json(sc, s7_car(current))?);
629 current = s7_cdr(current);
630 }
631 Ok(Value::Array(array))
632 }
633 } else if s7_is_byte_vector(obj) {
634 let mut hex_string = String::new();
635 let len = s7_vector_length(obj);
636
637 for i in 0..len {
638 let byte = s7_byte_vector_ref(obj, i);
639 write!(&mut hex_string, "{:02x}", byte).unwrap();
640 }
641
642 let mut special_type = Map::new();
643 special_type.insert("*type/byte-vector*".to_string(), Value::String(hex_string));
644 Ok(Value::Object(special_type))
645 } else if s7_is_vector(obj) {
646 let mut special_type = Map::new();
647 let mut array = Vec::new();
648 let len = s7_vector_length(obj);
649
650 for i in 0..len {
651 array.push(s7_obj_to_json(sc, s7_vector_ref(sc, obj, i))?);
652 }
653
654 special_type.insert("*type/vector*".to_string(), Value::Array(array));
655 Ok(Value::Object(special_type))
656 } else if s7_is_rational(obj) {
657 let rational_str = obj2str(sc, obj);
659 let mut special_type = Map::new();
660 special_type.insert("*type/rational*".to_string(), Value::String(rational_str));
661 Ok(Value::Object(special_type))
662 } else if s7_is_complex(obj) {
663 let complex_str = obj2str(sc, obj);
665 let mut special_type = Map::new();
666 special_type.insert("*type/complex*".to_string(), Value::String(complex_str));
667 Ok(Value::Object(special_type))
668 } else if s7_is_hash_table(obj) {
669 let mut special_type = Map::new();
671 let mut pairs = Vec::new();
672
673 special_type.insert("*type/hash-table*".to_string(), Value::Array(pairs));
676 Ok(Value::Object(special_type))
677 } else if s7_is_syntax(obj) {
678 let expr = obj2str(sc, obj);
680 let trimmed = expr.trim_start();
681 let quoted_inner = if let Some(rest) = trimmed.strip_prefix('\'') {
682 let rest = rest.trim_start();
683 if rest.is_empty() {
684 return Err("Empty quoted expression".to_string());
685 }
686 rest
687 } else if let Some(rest) = trimmed.strip_prefix("(quote ") {
688 let rest = rest.trim_end();
689 if let Some(rest) = rest.strip_suffix(')') {
690 rest.trim()
691 } else {
692 return Err("Malformed quote syntax".to_string());
693 }
694 } else {
695 return Err(format!("Unsupported syntax object: {}", expr));
696 };
697
698 let quoted_json = lisp2json(quoted_inner)?;
699 let mut special_type = Map::new();
700 special_type.insert("*type/quoted*".to_string(), quoted_json);
701 Ok(Value::Object(special_type))
702 } else {
703 let type_info = if s7_is_procedure(obj) {
705 "procedure"
706 } else if s7_is_macro(sc, obj) {
707 "macro"
708 } else {
709 "unknown"
710 };
711
712 Err(format!(
713 "Unknown Scheme type '{}' - cannot convert to JSON",
714 type_info
715 ))
716 }
717 }
718}
719
720unsafe fn json_to_s7_obj(sc: *mut s7_scheme, json: &Value) -> Result<s7_pointer, String> {
721 unsafe {
722 match json {
723 Value::Null => Ok(s7_nil(sc)),
724 Value::Bool(b) => Ok(s7_make_boolean(sc, *b)),
725 Value::Number(n) => {
726 if let Some(i) = n.as_i64() {
727 Ok(s7_make_integer(sc, i))
728 } else if let Some(f) = n.as_f64() {
729 Ok(s7_make_real(sc, f))
730 } else {
731 Err("Invalid number format in JSON".to_string())
732 }
733 }
734 Value::String(s) => match CString::new(s.as_str()) {
735 Ok(c_str) => Ok(s7_make_symbol(sc, c_str.as_ptr())),
736 Err(_) => Err("Invalid string format in JSON - contains null bytes".to_string()),
737 },
738 Value::Array(arr) => {
739 if arr.is_empty() {
740 Ok(s7_nil(sc))
741 } else {
742 let mut result = s7_nil(sc);
744
745 for item in arr.iter().rev() {
747 let s7_item = json_to_s7_obj(sc, item)?;
748 result = s7_cons(sc, s7_item, result);
749 }
750 Ok(result)
751 }
752 }
753 Value::Object(obj) => {
754 if obj.len() == 1 {
756 if let Some(Value::String(s)) = obj.get("*type/string*") {
757 match CString::new(s.as_str()) {
758 Ok(c_str) => return Ok(s7_make_string(sc, c_str.as_ptr())),
759 Err(_) => {
760 return Err(
761 "Invalid string in *type/string* - contains null bytes"
762 .to_string(),
763 );
764 }
765 }
766 }
767 if let Some(Value::Array(arr)) = obj.get("*type/vector*") {
768 let len = arr.len() as i64;
769 let vector = s7_make_vector(sc, len);
770 for (i, item) in arr.iter().enumerate() {
771 let s7_item = json_to_s7_obj(sc, item)?;
772 s7_vector_set(sc, vector, i as i64, s7_item);
773 }
774 return Ok(vector);
775 }
776 if let Some(value) = obj.get("*type/quoted*") {
777 let quoted = json_to_s7_obj(sc, value)?;
778 let quote_sym = s7_make_symbol(sc, c"quote".as_ptr());
779 let quoted_list = s7_cons(sc, quoted, s7_nil(sc));
780 return Ok(s7_cons(sc, quote_sym, quoted_list));
781 }
782 if let Some(Value::String(hex)) = obj.get("*type/byte-vector*") {
783 let bytes: Result<Vec<u8>, ParseIntError> = (0..hex.len())
784 .step_by(2)
785 .map(|i| {
786 if i + 2 <= hex.len() {
787 u8::from_str_radix(&hex[i..i + 2], 16)
788 } else if i + 1 <= hex.len() {
789 u8::from_str_radix(&hex[i..i + 1], 16)
791 } else {
792 Ok(0)
793 }
794 })
795 .collect();
796
797 match bytes {
798 Ok(bytes) => {
799 let len = bytes.len() as i64;
800 let bv = s7_make_byte_vector(sc, len, 1, std::ptr::null_mut());
801
802 for (i, &byte) in bytes.iter().enumerate() {
803 s7_byte_vector_set(bv, i as i64, byte);
804 }
805
806 return Ok(bv);
807 }
808 Err(_) => {
809 return Err("Invalid hex string in *type/byte-vector*".to_string());
810 }
811 }
812 }
813 if let Some(Value::Array(arr)) = obj.get("*type/pair*") {
814 if arr.len() == 2 {
815 let car = json_to_s7_obj(sc, &arr[0])?;
816 let cdr = json_to_s7_obj(sc, &arr[1])?;
817 return Ok(s7_cons(sc, car, cdr));
818 } else {
819 return Err("*type/pair* must contain exactly 2 elements".to_string());
820 }
821 }
822 }
823
824 if obj.is_empty() {
825 Ok(s7_nil(sc))
826 } else {
827 let mut result = s7_nil(sc);
829
830 for (key, value) in obj.iter().rev() {
831 let key_symbol = match CString::new(key.as_str()) {
832 Ok(c_key) => s7_make_symbol(sc, c_key.as_ptr()),
833 Err(_) => {
834 return Err(format!("Invalid key '{}' - contains null bytes", key));
835 }
836 };
837 let value_obj = json_to_s7_obj(sc, value)?;
838 let value_list = s7_cons(sc, value_obj, s7_nil(sc));
840 let pair = s7_cons(sc, key_symbol, value_list);
841 result = s7_cons(sc, pair, result);
842 }
843 Ok(result)
844 }
845 }
846 }
847 }
848}
849
850unsafe fn is_proper_assoc_list(sc: *mut s7_scheme, obj: s7_pointer) -> bool {
851 unsafe {
852 if s7_is_null(sc, obj) {
853 return true;
854 }
855
856 let mut current = obj;
857 while !s7_is_null(sc, current) {
858 if !s7_is_pair(current) {
859 return false;
860 }
861
862 let car = s7_car(current);
863 if !s7_is_pair(car) {
864 return false;
865 }
866
867 let key = s7_car(car);
868 if !s7_is_symbol(key) {
869 return false;
870 }
871
872 let cdr_part = s7_cdr(car);
873 if !s7_is_pair(cdr_part) || !s7_is_null(sc, s7_cdr(cdr_part)) {
875 return false;
876 }
877
878 current = s7_cdr(current);
879 }
880 true
881 }
882}
883
884static REMOVE: [&'static CStr; 84] = [
885 c"*autoload*",
886 c"*autoload-hook*",
887 c"*cload-directory*",
888 c"*features*",
889 c"*function*",
890 c"*libraries*",
891 c"*load-hook*",
892 c"*load-path*",
893 c"*stderr*",
894 c"*stdin*",
895 c"*stdout*",
896 c"abort",
897 c"autoload",
898 c"c-object-type",
899 c"c-object?",
900 c"c-pointer",
901 c"c-pointer->list",
902 c"c-pointer-info",
903 c"c-pointer-type",
904 c"c-pointer-weak1",
905 c"c-pointer-weak2",
906 c"c-pointer?",
907 c"call-with-current-continuation",
908 c"call-with-exit",
909 c"call-with-input-file",
910 c"call-with-input-file",
911 c"call-with-input-string",
912 c"call-with-output-file",
913 c"call-with-output-string",
914 c"call/cc",
915 c"close-input-port",
916 c"close-output-port",
917 c"continuation?",
918 c"current-error-port",
919 c"current-input-port",
920 c"current-output-port",
921 c"dilambda",
922 c"dilambda?",
923 c"dynamic-unwind",
924 c"dynamic-wind",
925 c"emergency-exit",
926 c"exit",
927 c"flush-output-port",
928 c"gc",
929 c"get-output-string",
930 c"goto?",
931 c"hook-functions",
932 c"input-port?",
933 c"load",
934 c"make-hook",
935 c"open-input-file",
936 c"open-input-function",
937 c"open-output-file",
938 c"open-output-function",
939 c"open-output-string",
940 c"output-port?",
941 c"owlet",
942 c"pair-filename",
943 c"pair-line-number",
944 c"peek-char",
945 c"port-closed?",
946 c"port-file",
947 c"port-filename",
948 c"port-line-number",
949 c"port-position",
950 c"profile-in",
951 c"random",
952 c"read-char",
953 c"read-string",
954 c"read-byte",
955 c"read-line",
956 c"require",
957 c"s7-optimize",
958 c"set-current-error-port",
959 c"stacktrace",
960 c"unlet",
961 c"with-baffle",
962 c"with-input-from-file",
963 c"with-output-to-file",
964 c"with-output-to-string",
965 c"write",
966 c"write-byte",
967 c"write-char",
968 c"write-string",
969];