Skip to content

Commit 5da2a36

Browse files
committed
fix some bugs with lisp/json parsing
1 parent bdfee14 commit 5da2a36

File tree

3 files changed

+94
-11
lines changed

3 files changed

+94
-11
lines changed

src/evaluator.rs

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,31 @@ pub fn lisp2json(expression: &str) -> Result<Value, String> {
106106
// - @byte-vector: {"*type/byte-vector*: "deadbeef0000" }
107107
// - @float-vector: {"*type/float-vector*": [3.2, 8.6, 0.1]}
108108
// - @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+
};
109128

110129
unsafe {
111130
let sc: *mut s7_scheme = s7_init();
112131

113132
// Parse the expression without evaluating it
114-
let c_expr = CString::new(expression).unwrap_or_else(|_| CString::new("()").unwrap());
133+
let c_expr = CString::new(expr).unwrap_or_else(|_| CString::new("()").unwrap());
115134
let input_port = s7_open_input_string(sc, c_expr.as_ptr());
116135
let s7_obj = s7_read(sc, input_port);
117136
s7_close_input_port(sc, input_port);
@@ -530,21 +549,37 @@ unsafe fn s7_obj_to_json(sc: *mut s7_scheme, obj: s7_pointer) -> Result<Value, S
530549
} else if s7_is_pair(obj) {
531550
// Check if it's a quote form first
532551
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+
}
533567
if s7_is_symbol(car) {
534568
let symbol_name_ptr = s7_symbol_name(car);
535569
if !symbol_name_ptr.is_null() {
536570
let symbol_name = CStr::from_ptr(symbol_name_ptr).to_string_lossy();
537571

538572
if symbol_name == "quote" {
539-
// Handle quote form: convert (quote expr) to ["quote", expr]
573+
// Handle quote form: convert (quote expr) to {"*type/quoted*": <expr>}
540574
let cdr = s7_cdr(obj);
541575
if s7_is_pair(cdr) && s7_is_null(sc, s7_cdr(cdr)) {
542-
// It's a proper quote form: (quote expr)
543576
let quoted_expr = s7_car(cdr);
544-
let mut array = Vec::new();
545-
array.push(Value::String("quote".to_string()));
546-
array.push(s7_obj_to_json(sc, quoted_expr)?);
547-
return Ok(Value::Array(array));
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));
548583
}
549584
}
550585
}
@@ -639,6 +674,31 @@ unsafe fn s7_obj_to_json(sc: *mut s7_scheme, obj: s7_pointer) -> Result<Value, S
639674
// This is a simplified approach - we'd need to iterate through the hash table
640675
special_type.insert("*type/hash-table*".to_string(), Value::Array(pairs));
641676
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))
642702
} else {
643703
// For debugging: let's see what type this actually is
644704
let type_info = if s7_is_procedure(obj) {
@@ -713,6 +773,12 @@ unsafe fn json_to_s7_obj(sc: *mut s7_scheme, json: &Value) -> Result<s7_pointer,
713773
}
714774
return Ok(vector);
715775
}
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+
}
716782
if let Some(Value::String(hex)) = obj.get("*type/byte-vector*") {
717783
let bytes: Result<Vec<u8>, ParseIntError> = (0..hex.len())
718784
.step_by(2)

src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl Journal {
149149
"Failed to parse Scheme to JSON. Result: {}",
150150
result
151151
);
152-
lisp2json("(error parse-error \"Failed to parse Scheme to JSON\")")
152+
lisp2json("(error 'parse-error \"Failed to parse Scheme to JSON\")")
153153
}
154154
.expect("Error parsing the JSON error message"),
155155
}
@@ -161,7 +161,7 @@ impl Journal {
161161
"Failed to parse JSON to Scheme. Query: {}",
162162
query_str
163163
);
164-
lisp2json("(error parse-error \"Failed to parse JSON to Scheme\")")
164+
lisp2json("(error 'parse-error \"Failed to parse JSON to Scheme\")")
165165
}
166166
.expect("Error parsing the JSON error message"),
167167
}
@@ -185,7 +185,7 @@ impl Journal {
185185
"Failed to parse Scheme to JSON. Query: {}",
186186
query
187187
);
188-
lisp2json("(error parse-error \"Failed to parse Scheme to JSON\")")
188+
lisp2json("(error 'parse-error \"Failed to parse Scheme to JSON\")")
189189
}
190190
.expect("Error parsing the JSON error message"),
191191
}
@@ -211,7 +211,7 @@ impl Journal {
211211
"Failed to parse JSON to Scheme. Query: {}",
212212
query_str
213213
);
214-
"(error parse-error \"Failed to parse JSON to Scheme\")".to_string()
214+
"(error 'parse-error \"Failed to parse JSON to Scheme\")".to_string()
215215
}
216216
}
217217
}

tests/json.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ fn test_special_type_round_trip() {
190190
let scheme = json2lisp(&original.clone()).unwrap();
191191
let back_to_json = lisp2json(&scheme).unwrap();
192192
assert_eq!(original, back_to_json);
193+
194+
// Test quote special type round trip
195+
let original = json!({"*type/quoted*": ["+", 2, 2]});
196+
let scheme = json2lisp(&original.clone()).unwrap();
197+
let back_to_json = lisp2json(&scheme).unwrap();
198+
assert_eq!(original, back_to_json);
193199
}
194200

195201
#[test]
@@ -289,4 +295,15 @@ fn test_quote_handling() {
289295
// Test number
290296
let json_val = lisp2json("42").unwrap();
291297
assert_eq!(json_val, json!(42));
298+
299+
// Test quoted list
300+
let json_val = lisp2json("'(+ 2 2)").unwrap();
301+
assert_eq!(json_val, json!({"*type/quoted*": ["+", 2, 2]}));
302+
303+
// Test nested quote in list
304+
let json_val = lisp2json("(symbol-a 'symbol-b)").unwrap();
305+
assert_eq!(
306+
json_val,
307+
json!(["symbol-a", {"*type/quoted*": "symbol-b"}])
308+
);
292309
}

0 commit comments

Comments
 (0)