1use serde_json::{Map, Value as JsonValue, json};
8use sim_codec::DecodeBudget;
9use sim_kernel::{
10 Expr, LocatedExpr, LocatedExprTree, NumberLiteral, Origin, Result, SourceId, Span, Trivia,
11};
12
13use crate::expr_json::{expr_to_json, json_to_expr};
14use crate::helpers::{
15 base64_decode, base64_encode, json_error, json_to_symbol, local_to_json, optional_bytes,
16 parse_quote_mode, parse_symbol, quote_mode_name, required_array, required_bool, required_str,
17 required_u64, required_value, symbol_from_json, symbol_to_json,
18};
19
20pub fn located_expr_to_json(located: &LocatedExpr, include_origin: bool) -> JsonValue {
23 if !include_origin {
24 return expr_to_json(&located.expr);
25 }
26
27 let mut object = Map::new();
28 object.insert("$located".to_owned(), expr_to_json(&located.expr));
29 if let Some(origin) = &located.origin {
30 object.insert("origin".to_owned(), origin_to_json(origin));
31 }
32 JsonValue::Object(object)
33}
34
35pub fn tree_to_json(tree: &LocatedExprTree, include_origin: bool) -> JsonValue {
38 tree_expr_to_json(tree, include_origin)
39}
40
41pub fn json_to_located_expr(
44 codec: sim_kernel::CodecId,
45 value: &JsonValue,
46 budget: &mut DecodeBudget,
47 depth: usize,
48) -> Result<LocatedExpr> {
49 if let Some(object) = value.as_object()
50 && let Some(expr_value) = object.get("$located")
51 {
52 return Ok(LocatedExpr {
53 expr: json_to_expr(codec, expr_value, budget, depth)?,
54 origin: object
55 .get("origin")
56 .map(|origin| json_to_origin(codec, origin, budget))
57 .transpose()?,
58 });
59 }
60
61 Ok(LocatedExpr {
62 expr: json_to_expr(codec, value, budget, depth)?,
63 origin: None,
64 })
65}
66
67pub fn json_to_tree(
70 codec: sim_kernel::CodecId,
71 value: &JsonValue,
72 budget: &mut DecodeBudget,
73 depth: usize,
74) -> Result<LocatedExprTree> {
75 if let Some(object) = value.as_object()
76 && let Some(expr_value) = object.get("$located")
77 {
78 let mut tree = json_expr_to_tree(codec, expr_value, budget, depth)?;
79 tree.origin = object
80 .get("origin")
81 .map(|origin| json_to_origin(codec, origin, budget))
82 .transpose()?;
83 return Ok(tree);
84 }
85 json_expr_to_tree(codec, value, budget, depth)
86}
87
88fn json_expr_to_tree(
89 codec: sim_kernel::CodecId,
90 value: &JsonValue,
91 budget: &mut DecodeBudget,
92 depth: usize,
93) -> Result<LocatedExprTree> {
94 budget.enter_node(codec, depth)?;
95 let Some(tag) = value.get("$expr").and_then(JsonValue::as_str) else {
96 return Err(json_error(codec, "missing $expr"));
97 };
98
99 match tag {
100 "nil" => Ok(LocatedExprTree::without_children(Expr::Nil, None)),
101 "bool" => Ok(LocatedExprTree::without_children(
102 Expr::Bool(required_bool(codec, value, "value")?),
103 None,
104 )),
105 "number" => Ok(LocatedExprTree::without_children(
106 Expr::Number(NumberLiteral {
107 domain: parse_symbol(required_str(codec, value, "domain")?),
108 canonical: required_str(codec, value, "value")?.to_owned(),
109 }),
110 None,
111 )),
112 "symbol" => Ok(LocatedExprTree::without_children(
113 json_to_symbol(codec, value)?,
114 None,
115 )),
116 "local" => Ok(LocatedExprTree::without_children(
117 Expr::Local(symbol_from_json(codec, value)?),
118 None,
119 )),
120 "string" => {
121 let text = required_str(codec, value, "value")?;
122 budget.check_string_bytes(codec, text.len())?;
123 Ok(LocatedExprTree::without_children(
124 Expr::String(text.to_owned()),
125 None,
126 ))
127 }
128 "bytes" => {
129 let encoded = required_str(codec, value, "base64")?;
130 budget.check_blob_bytes(codec, encoded.len())?;
131 let decoded = base64_decode(codec, encoded)?;
132 budget.check_blob_bytes(codec, decoded.len())?;
133 Ok(LocatedExprTree::without_children(
134 Expr::Bytes(decoded),
135 None,
136 ))
137 }
138 "list" => tree_sequence(codec, value, "items", Expr::List, budget, depth + 1),
139 "vector" => tree_sequence(codec, value, "items", Expr::Vector, budget, depth + 1),
140 "map" => {
141 let entries = required_array(codec, value, "entries")?;
142 budget.check_collection_len(codec, entries.len())?;
143 let entries = entries
144 .iter()
145 .map(|entry| {
146 Ok((
147 json_to_tree(
148 codec,
149 required_value(codec, entry, "key")?,
150 budget,
151 depth + 1,
152 )?,
153 json_to_tree(
154 codec,
155 required_value(codec, entry, "value")?,
156 budget,
157 depth + 1,
158 )?,
159 ))
160 })
161 .collect::<Result<Vec<_>>>()?;
162 Ok(LocatedExprTree {
163 expr: Expr::Map(
164 entries
165 .iter()
166 .map(|(key, value)| (key.expr.clone(), value.expr.clone()))
167 .collect(),
168 ),
169 origin: None,
170 children: entries
171 .into_iter()
172 .flat_map(|(key, value)| [key, value])
173 .collect(),
174 })
175 }
176 "set" => tree_sequence(codec, value, "items", Expr::Set, budget, depth + 1),
177 "call" => {
178 let operator = json_to_tree(
179 codec,
180 required_value(codec, value, "operator")?,
181 budget,
182 depth + 1,
183 )?;
184 let raw_args = required_array(codec, value, "args")?;
185 budget.check_collection_len(codec, raw_args.len())?;
186 let args = raw_args
187 .iter()
188 .map(|arg| json_to_tree(codec, arg, budget, depth + 1))
189 .collect::<Result<Vec<_>>>()?;
190 let mut children = Vec::with_capacity(args.len() + 1);
191 children.push(operator.clone());
192 children.extend(args.iter().cloned());
193 Ok(LocatedExprTree {
194 expr: Expr::Call {
195 operator: Box::new(operator.expr.clone()),
196 args: args.iter().map(|arg| arg.expr.clone()).collect(),
197 },
198 origin: None,
199 children,
200 })
201 }
202 "infix" => {
203 let left = json_to_tree(
204 codec,
205 required_value(codec, value, "left")?,
206 budget,
207 depth + 1,
208 )?;
209 let right = json_to_tree(
210 codec,
211 required_value(codec, value, "right")?,
212 budget,
213 depth + 1,
214 )?;
215 Ok(LocatedExprTree {
216 expr: Expr::Infix {
217 operator: symbol_from_json(codec, required_value(codec, value, "operator")?)?,
218 left: Box::new(left.expr.clone()),
219 right: Box::new(right.expr.clone()),
220 },
221 origin: None,
222 children: vec![left, right],
223 })
224 }
225 "prefix" => {
226 let arg = json_to_tree(
227 codec,
228 required_value(codec, value, "arg")?,
229 budget,
230 depth + 1,
231 )?;
232 Ok(LocatedExprTree {
233 expr: Expr::Prefix {
234 operator: symbol_from_json(codec, required_value(codec, value, "operator")?)?,
235 arg: Box::new(arg.expr.clone()),
236 },
237 origin: None,
238 children: vec![arg],
239 })
240 }
241 "postfix" => {
242 let arg = json_to_tree(
243 codec,
244 required_value(codec, value, "arg")?,
245 budget,
246 depth + 1,
247 )?;
248 Ok(LocatedExprTree {
249 expr: Expr::Postfix {
250 operator: symbol_from_json(codec, required_value(codec, value, "operator")?)?,
251 arg: Box::new(arg.expr.clone()),
252 },
253 origin: None,
254 children: vec![arg],
255 })
256 }
257 "block" => tree_sequence(codec, value, "items", Expr::Block, budget, depth + 1),
258 "quote" => {
259 let expr = json_to_tree(
260 codec,
261 required_value(codec, value, "expr")?,
262 budget,
263 depth + 1,
264 )?;
265 Ok(LocatedExprTree {
266 expr: Expr::Quote {
267 mode: parse_quote_mode(codec, required_str(codec, value, "mode")?)?,
268 expr: Box::new(expr.expr.clone()),
269 },
270 origin: None,
271 children: vec![expr],
272 })
273 }
274 "annotated" => {
275 let expr = json_to_tree(
276 codec,
277 required_value(codec, value, "expr")?,
278 budget,
279 depth + 1,
280 )?;
281 let annotations = required_array(codec, value, "annotations")?;
282 budget.check_collection_len(codec, annotations.len())?;
283 let annotations = annotations
284 .iter()
285 .map(|entry| {
286 Ok((
287 symbol_from_json(codec, required_value(codec, entry, "key")?)?,
288 json_to_tree(
289 codec,
290 required_value(codec, entry, "value")?,
291 budget,
292 depth + 1,
293 )?,
294 ))
295 })
296 .collect::<Result<Vec<_>>>()?;
297 let mut children = Vec::with_capacity(annotations.len() + 1);
298 children.push(expr.clone());
299 children.extend(annotations.iter().map(|(_, value)| value.clone()));
300 Ok(LocatedExprTree {
301 expr: Expr::Annotated {
302 expr: Box::new(expr.expr.clone()),
303 annotations: annotations
304 .iter()
305 .map(|(key, value)| (key.clone(), value.expr.clone()))
306 .collect(),
307 },
308 origin: None,
309 children,
310 })
311 }
312 "extension" => {
313 let payload = json_to_tree(
314 codec,
315 required_value(codec, value, "payload")?,
316 budget,
317 depth + 1,
318 )?;
319 Ok(LocatedExprTree {
320 expr: Expr::Extension {
321 tag: symbol_from_json(codec, required_value(codec, value, "tag")?)?,
322 payload: Box::new(payload.expr.clone()),
323 },
324 origin: None,
325 children: vec![payload],
326 })
327 }
328 other => Err(json_error(codec, format!("unknown expr tag {other}"))),
329 }
330}
331
332fn tree_expr_to_json(tree: &LocatedExprTree, include_origin: bool) -> JsonValue {
333 let expr_value = match &tree.expr {
334 Expr::Nil => json!({ "$expr": "nil" }),
335 Expr::Bool(value) => json!({ "$expr": "bool", "value": value }),
336 Expr::Number(number) => json!({
337 "$expr": "number",
338 "domain": number.domain.to_string(),
339 "value": number.canonical,
340 }),
341 Expr::Symbol(symbol) => symbol_to_json(symbol),
342 Expr::Local(symbol) => local_to_json(symbol),
343 Expr::String(value) => json!({ "$expr": "string", "value": value }),
344 Expr::Bytes(value) => json!({ "$expr": "bytes", "base64": base64_encode(value) }),
345 Expr::List(_) => tree_sequence_json("list", &tree.children, include_origin),
346 Expr::Vector(_) => tree_sequence_json("vector", &tree.children, include_origin),
347 Expr::Map(_) => json!({
348 "$expr": "map",
349 "entries": tree.children.chunks(2).map(|pair| json!({
350 "key": tree_to_json(&pair[0], include_origin),
351 "value": tree_to_json(&pair[1], include_origin),
352 })).collect::<Vec<_>>(),
353 }),
354 Expr::Set(_) => tree_sequence_json("set", &tree.children, include_origin),
355 Expr::Call { .. } => json!({
356 "$expr": "call",
357 "operator": tree_to_json(&tree.children[0], include_origin),
358 "args": tree.children[1..].iter().map(|item| tree_to_json(item, include_origin)).collect::<Vec<_>>(),
359 }),
360 Expr::Infix { operator, .. } => json!({
361 "$expr": "infix",
362 "operator": symbol_to_json(operator),
363 "left": tree_to_json(&tree.children[0], include_origin),
364 "right": tree_to_json(&tree.children[1], include_origin),
365 }),
366 Expr::Prefix { operator, .. } => json!({
367 "$expr": "prefix",
368 "operator": symbol_to_json(operator),
369 "arg": tree_to_json(&tree.children[0], include_origin),
370 }),
371 Expr::Postfix { operator, .. } => json!({
372 "$expr": "postfix",
373 "operator": symbol_to_json(operator),
374 "arg": tree_to_json(&tree.children[0], include_origin),
375 }),
376 Expr::Block(_) => tree_sequence_json("block", &tree.children, include_origin),
377 Expr::Quote { mode, .. } => json!({
378 "$expr": "quote",
379 "mode": quote_mode_name(*mode),
380 "expr": tree_to_json(&tree.children[0], include_origin),
381 }),
382 Expr::Annotated { annotations, .. } => json!({
383 "$expr": "annotated",
384 "expr": tree_to_json(&tree.children[0], include_origin),
385 "annotations": annotations.iter().zip(tree.children[1..].iter()).map(|((key, _), value)| json!({
386 "key": symbol_to_json(key),
387 "value": tree_to_json(value, include_origin),
388 })).collect::<Vec<_>>(),
389 }),
390 Expr::Extension { tag, .. } => json!({
391 "$expr": "extension",
392 "tag": symbol_to_json(tag),
393 "payload": tree_to_json(&tree.children[0], include_origin),
394 }),
395 };
396 if include_origin && let Some(origin) = tree.origin.as_ref() {
397 let mut object = Map::new();
398 object.insert("$located".to_owned(), expr_value);
399 object.insert("origin".to_owned(), origin_to_json(origin));
400 JsonValue::Object(object)
401 } else {
402 expr_value
403 }
404}
405
406fn tree_sequence_json(
407 tag: &'static str,
408 items: &[LocatedExprTree],
409 include_origin: bool,
410) -> JsonValue {
411 json!({
412 "$expr": tag,
413 "items": items.iter().map(|item| tree_to_json(item, include_origin)).collect::<Vec<_>>(),
414 })
415}
416
417fn tree_sequence(
418 codec: sim_kernel::CodecId,
419 value: &JsonValue,
420 field: &str,
421 build: fn(Vec<Expr>) -> Expr,
422 budget: &mut DecodeBudget,
423 depth: usize,
424) -> Result<LocatedExprTree> {
425 let children = required_array(codec, value, field)?;
426 budget.check_collection_len(codec, children.len())?;
427 let children = children
428 .iter()
429 .map(|item| json_to_tree(codec, item, budget, depth))
430 .collect::<Result<Vec<_>>>()?;
431 Ok(LocatedExprTree {
432 expr: build(children.iter().map(|item| item.expr.clone()).collect()),
433 origin: None,
434 children,
435 })
436}
437
438fn origin_to_json(origin: &Origin) -> JsonValue {
439 json!({
440 "codec": origin.codec.0,
441 "source": origin.source.0,
442 "span": {
443 "start": origin.span.start,
444 "end": origin.span.end,
445 },
446 "trivia": origin.trivia.iter().map(trivia_to_json).collect::<Vec<_>>(),
447 })
448}
449
450fn trivia_to_json(trivia: &Trivia) -> JsonValue {
451 match trivia {
452 Trivia::Whitespace(text) => json!({ "kind": "whitespace", "text": text }),
453 Trivia::LineComment(text) => json!({ "kind": "line-comment", "text": text }),
454 Trivia::BlockComment(text) => json!({ "kind": "block-comment", "text": text }),
455 }
456}
457
458fn json_to_origin(
459 codec: sim_kernel::CodecId,
460 value: &JsonValue,
461 budget: &mut DecodeBudget,
462) -> Result<Origin> {
463 let trivia = required_array(codec, value, "trivia")?;
464 budget.check_collection_len(codec, trivia.len())?;
465 let origin = Origin {
466 codec: sim_kernel::CodecId(required_u64(codec, value, "codec")? as u32),
467 source: SourceId(required_str(codec, value, "source")?.to_owned()),
468 span: {
469 let span = required_value(codec, value, "span")?;
470 Span {
471 start: required_u64(codec, span, "start")? as usize,
472 end: required_u64(codec, span, "end")? as usize,
473 }
474 },
475 trivia: trivia
476 .iter()
477 .map(|item| json_to_trivia(codec, item, budget))
478 .collect::<Result<Vec<_>>>()?,
479 };
480 if let Some(raw) = optional_bytes(codec, value, "raw")? {
481 let mut registry = sim_kernel::SourceRegistry::default();
482 registry.intern_span(&origin, &raw);
483 }
484 Ok(origin)
485}
486
487fn json_to_trivia(
488 codec: sim_kernel::CodecId,
489 value: &JsonValue,
490 budget: &mut DecodeBudget,
491) -> Result<Trivia> {
492 budget.add_trivia(codec)?;
493 let kind = required_str(codec, value, "kind")?;
494 let text = required_str(codec, value, "text")?.to_owned();
495 match kind {
496 "whitespace" => Ok(Trivia::Whitespace(text)),
497 "line-comment" => Ok(Trivia::LineComment(text)),
498 "block-comment" => Ok(Trivia::BlockComment(text)),
499 other => Err(json_error(codec, format!("unknown trivia kind {other}"))),
500 }
501}