journal_sdk/
evaluator.rs

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    // <TYPE>: <JSON>
94    // ------------------------------------------------------
95    // symbol: "string"
96    // number: 5.5
97    // boolean: true/false
98    // list: []
99    // symbol assoc lists: { }
100    // special types
101    // - @pair: {"*type/pair*": ["first", "second"]}
102    // - @string: { "*type/string*": "this is my string" }
103    // - @rational: { "*type/rational*": "5/5" }
104    // - @complex: { "*type/complex*": "5/5" }
105    // - @vector: {"*type/vector*: ["blah", "blah"]}
106    // - @byte-vector: {"*type/byte-vector*: "deadbeef0000" }
107    // - @float-vector: {"*type/float-vector*": [3.2, 8.6, 0.1]}
108    // - @hash-table: {"*type/hash-table*": [["a", 6], [53, 199]]}
109    // - @quoted: {"*type/quoted*": [["a", 6], [53, 199]]}
110
111    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        // Parse the expression without evaluating it
133        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            // remove insecure primitives
184            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            // add new types
194            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            // add new primitives
204            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                // execute query and return
233                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 s7_c_str = s7_string(s7_object_to_string(sc, arg, false));
259        // let c_string = CStr::from_ptr(s7_c_str);
260        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]; // quote so that it evaluates correctly
294        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        // todo: this might cause a pointer issue
401        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 c_str = s7_string(obj);
535            // let rust_str = CStr::from_ptr(c_str).to_string_lossy();
536            let rust_str = obj2str(sc, obj);
537
538            // Check if it's a special type marker
539            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            // Check if it's a quote form first
551            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                        // Handle quote form: convert (quote expr) to {"*type/quoted*": <expr>}
574                        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            // Check if it's an association list with proper list format (for JSON objects)
589            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                        // Only handle list format (key value)
600                        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                // Handle pairs as special type
616                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                // Regular list - convert to JSON array
624                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            // Handle rational numbers as special type
658            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            // Handle complex numbers as special type
664            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            // Handle hash tables as special type
670            let mut special_type = Map::new();
671            let mut pairs = Vec::new();
672
673            // Convert hash table to array of [key, value] pairs
674            // This is a simplified approach - we'd need to iterate through the hash table
675            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            // Fallback for syntax objects (e.g., nested quote shorthand).
679            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            // For debugging: let's see what type this actually is
704            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                    // Create (list ...) expression
743                    let mut result = s7_nil(sc);
744
745                    // Build arguments in reverse order
746                    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                // Check for special type markers
755                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                                    // Handle odd-length hex string
790                                    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                    // Regular object - convert to association list
828                    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                        // Create a list (key value) instead of a pair (key . value)
839                        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            // Only accept proper list format (key value)
874            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];