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;
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    // <TYPE>: <JSON>
93    // ------------------------------------------------------
94    // symbol: "string"
95    // number: 5.5
96    // boolean: true/false
97    // list: []
98    // symbol assoc lists: { }
99    // special types
100    // - @pair: {"*type/pair*": ["first", "second"]}
101    // - @string: { "*type/string*": "this is my string" }
102    // - @rational: { "*type/rational*": "5/5" }
103    // - @complex: { "*type/complex*": "5/5" }
104    // - @vector: {"*type/vector*: ["blah", "blah"]}
105    // - @byte-vector: {"*type/byte-vector*: "deadbeef0000" }
106    // - @float-vector: {"*type/float-vector*": [3.2, 8.6, 0.1]}
107    // - @hash-table: {"*type/hash-table*": [["a", 6], [53, 199]]}
108    // - @quoted: {"*type/quoted*": [["a", 6], [53, 199]]}
109
110    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        // Parse the expression without evaluating it
132        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            // remove insecure primitives
182            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            // add new types
192            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            // add new primitives
202            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                // execute query and return
231                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 s7_c_str = s7_string(s7_object_to_string(sc, arg, false));
257        // let c_string = CStr::from_ptr(s7_c_str);
258        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]; // quote so that it evaluates correctly
292        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        // todo: this might cause a pointer issue
399        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 c_str = s7_string(obj);
507            // let rust_str = CStr::from_ptr(c_str).to_string_lossy();
508            let rust_str = obj2str(sc, obj);
509
510            // Check if it's a special type marker
511            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            // Check if it's a quote form first
523            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                        // Handle quote form: convert (quote expr) to {"*type/quoted*": <expr>}
546                        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            // Check if it's an association list with proper list format (for JSON objects)
561            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                        // Only handle list format (key value)
572                        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                // Handle pairs as special type
588                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                // Regular list - convert to JSON array
596                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            // Handle rational numbers as special type
630            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            // Handle complex numbers as special type
636            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            // Handle hash tables as special type
642            let mut special_type = Map::new();
643            let mut pairs = Vec::new();
644
645            // Convert hash table to array of [key, value] pairs
646            // This is a simplified approach - we'd need to iterate through the hash table
647            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            // Fallback for syntax objects (e.g., nested quote shorthand).
651            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            // For debugging: let's see what type this actually is
676            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                    // Create (list ...) expression
715                    let mut result = s7_nil(sc);
716
717                    // Build arguments in reverse order
718                    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                // Check for special type markers
727                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                                    // Handle odd-length hex string
762                                    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                    // Regular object - convert to association list
800                    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                        // Create a list (key value) instead of a pair (key . value)
811                        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            // Only accept proper list format (key value)
846            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];