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