1use serde_json::{Value as JsonValue, json};
7use sim_codec::DecodeBudget;
8use sim_kernel::{Expr, NumberLiteral, Result};
9
10use crate::helpers::{
11 base64_decode, base64_encode, json_error, json_to_symbol, local_to_json, parse_quote_mode,
12 parse_symbol, quote_mode_name, required_array, required_bool, required_str, required_value,
13 symbol_from_json, symbol_to_json,
14};
15
16pub fn expr_to_json(expr: &Expr) -> JsonValue {
21 match expr {
22 Expr::Nil => json!({ "$expr": "nil" }),
23 Expr::Bool(value) => json!({ "$expr": "bool", "value": value }),
24 Expr::Number(number) => json!({
25 "$expr": "number",
26 "domain": number.domain.to_string(),
27 "value": number.canonical,
28 }),
29 Expr::Symbol(symbol) => symbol_to_json(symbol),
30 Expr::Local(symbol) => local_to_json(symbol),
31 Expr::String(value) => json!({ "$expr": "string", "value": value }),
32 Expr::Bytes(value) => json!({
33 "$expr": "bytes",
34 "base64": base64_encode(value),
35 }),
36 Expr::List(items) => sequence_json("list", items),
37 Expr::Vector(items) => sequence_json("vector", items),
38 Expr::Map(entries) => {
39 let mut sorted = entries.clone();
40 sorted.sort_by_key(|(key, value)| (key.canonical_key(), value.canonical_key()));
41 json!({
42 "$expr": "map",
43 "entries": sorted
44 .iter()
45 .map(|(key, value)| json!({
46 "key": expr_to_json(key),
47 "value": expr_to_json(value),
48 }))
49 .collect::<Vec<_>>(),
50 })
51 }
52 Expr::Set(items) => {
53 let mut sorted = items.clone();
54 sorted.sort_by_key(Expr::canonical_key);
55 sequence_json("set", &sorted)
56 }
57 Expr::Call { operator, args } => json!({
58 "$expr": "call",
59 "operator": expr_to_json(operator),
60 "args": args.iter().map(expr_to_json).collect::<Vec<_>>(),
61 }),
62 Expr::Infix {
63 operator,
64 left,
65 right,
66 } => json!({
67 "$expr": "infix",
68 "operator": symbol_to_json(operator),
69 "left": expr_to_json(left),
70 "right": expr_to_json(right),
71 }),
72 Expr::Prefix { operator, arg } => json!({
73 "$expr": "prefix",
74 "operator": symbol_to_json(operator),
75 "arg": expr_to_json(arg),
76 }),
77 Expr::Postfix { operator, arg } => json!({
78 "$expr": "postfix",
79 "operator": symbol_to_json(operator),
80 "arg": expr_to_json(arg),
81 }),
82 Expr::Block(items) => sequence_json("block", items),
83 Expr::Quote { mode, expr } => json!({
84 "$expr": "quote",
85 "mode": quote_mode_name(*mode),
86 "expr": expr_to_json(expr),
87 }),
88 Expr::Annotated { expr, annotations } => json!({
89 "$expr": "annotated",
90 "expr": expr_to_json(expr),
91 "annotations": annotations
92 .iter()
93 .map(|(key, value)| json!({
94 "key": symbol_to_json(key),
95 "value": expr_to_json(value),
96 }))
97 .collect::<Vec<_>>(),
98 }),
99 Expr::Extension { tag, payload } => json!({
100 "$expr": "extension",
101 "tag": symbol_to_json(tag),
102 "payload": expr_to_json(payload),
103 }),
104 }
105}
106
107pub fn json_to_expr(
113 codec: sim_kernel::CodecId,
114 value: &JsonValue,
115 budget: &mut DecodeBudget,
116 depth: usize,
117) -> Result<Expr> {
118 budget.enter_node(codec, depth)?;
119 let Some(tag) = value.get("$expr").and_then(JsonValue::as_str) else {
120 return Err(json_error(codec, "missing $expr"));
121 };
122
123 match tag {
124 "nil" => Ok(Expr::Nil),
125 "bool" => Ok(Expr::Bool(required_bool(codec, value, "value")?)),
126 "number" => Ok(Expr::Number(NumberLiteral {
127 domain: parse_symbol(required_str(codec, value, "domain")?),
128 canonical: required_str(codec, value, "value")?.to_owned(),
129 })),
130 "symbol" => Ok(json_to_symbol(codec, value)?),
131 "local" => Ok(Expr::Local(symbol_from_json(codec, value)?)),
132 "string" => {
133 let text = required_str(codec, value, "value")?;
134 budget.check_string_bytes(codec, text.len())?;
135 Ok(Expr::String(text.to_owned()))
136 }
137 "bytes" => {
138 let encoded = required_str(codec, value, "base64")?;
139 budget.check_blob_bytes(codec, encoded.len())?;
140 let decoded = base64_decode(codec, encoded)?;
141 budget.check_blob_bytes(codec, decoded.len())?;
142 Ok(Expr::Bytes(decoded))
143 }
144 "list" => Ok(Expr::List(read_items(
145 codec,
146 value,
147 "items",
148 budget,
149 depth + 1,
150 )?)),
151 "vector" => Ok(Expr::Vector(read_items(
152 codec,
153 value,
154 "items",
155 budget,
156 depth + 1,
157 )?)),
158 "map" => {
159 let entries = required_array(codec, value, "entries")?;
160 budget.check_collection_len(codec, entries.len())?;
161 Ok(Expr::Map(
162 entries
163 .iter()
164 .map(|entry| {
165 Ok((
166 json_to_expr(
167 codec,
168 required_value(codec, entry, "key")?,
169 budget,
170 depth + 1,
171 )?,
172 json_to_expr(
173 codec,
174 required_value(codec, entry, "value")?,
175 budget,
176 depth + 1,
177 )?,
178 ))
179 })
180 .collect::<Result<Vec<_>>>()?,
181 ))
182 }
183 "set" => Ok(Expr::Set(read_items(
184 codec,
185 value,
186 "items",
187 budget,
188 depth + 1,
189 )?)),
190 "call" => Ok(Expr::Call {
191 operator: Box::new(json_to_expr(
192 codec,
193 required_value(codec, value, "operator")?,
194 budget,
195 depth + 1,
196 )?),
197 args: read_items(codec, value, "args", budget, depth + 1)?,
198 }),
199 "infix" => Ok(Expr::Infix {
200 operator: symbol_from_json(codec, required_value(codec, value, "operator")?)?,
201 left: Box::new(json_to_expr(
202 codec,
203 required_value(codec, value, "left")?,
204 budget,
205 depth + 1,
206 )?),
207 right: Box::new(json_to_expr(
208 codec,
209 required_value(codec, value, "right")?,
210 budget,
211 depth + 1,
212 )?),
213 }),
214 "prefix" => Ok(Expr::Prefix {
215 operator: symbol_from_json(codec, required_value(codec, value, "operator")?)?,
216 arg: Box::new(json_to_expr(
217 codec,
218 required_value(codec, value, "arg")?,
219 budget,
220 depth + 1,
221 )?),
222 }),
223 "postfix" => Ok(Expr::Postfix {
224 operator: symbol_from_json(codec, required_value(codec, value, "operator")?)?,
225 arg: Box::new(json_to_expr(
226 codec,
227 required_value(codec, value, "arg")?,
228 budget,
229 depth + 1,
230 )?),
231 }),
232 "block" => Ok(Expr::Block(read_items(
233 codec,
234 value,
235 "items",
236 budget,
237 depth + 1,
238 )?)),
239 "quote" => Ok(Expr::Quote {
240 mode: parse_quote_mode(codec, required_str(codec, value, "mode")?)?,
241 expr: Box::new(json_to_expr(
242 codec,
243 required_value(codec, value, "expr")?,
244 budget,
245 depth + 1,
246 )?),
247 }),
248 "annotated" => {
249 let annotations = required_array(codec, value, "annotations")?;
250 budget.check_collection_len(codec, annotations.len())?;
251 Ok(Expr::Annotated {
252 expr: Box::new(json_to_expr(
253 codec,
254 required_value(codec, value, "expr")?,
255 budget,
256 depth + 1,
257 )?),
258 annotations: annotations
259 .iter()
260 .map(|entry| {
261 Ok((
262 symbol_from_json(codec, required_value(codec, entry, "key")?)?,
263 json_to_expr(
264 codec,
265 required_value(codec, entry, "value")?,
266 budget,
267 depth + 1,
268 )?,
269 ))
270 })
271 .collect::<Result<Vec<_>>>()?,
272 })
273 }
274 "extension" => Ok(Expr::Extension {
275 tag: symbol_from_json(codec, required_value(codec, value, "tag")?)?,
276 payload: Box::new(json_to_expr(
277 codec,
278 required_value(codec, value, "payload")?,
279 budget,
280 depth + 1,
281 )?),
282 }),
283 other => Err(json_error(codec, format!("unknown expr tag {other}"))),
284 }
285}
286
287fn sequence_json(tag: &'static str, items: &[Expr]) -> JsonValue {
288 json!({
289 "$expr": tag,
290 "items": items.iter().map(expr_to_json).collect::<Vec<_>>(),
291 })
292}
293
294fn read_items(
295 codec: sim_kernel::CodecId,
296 value: &JsonValue,
297 field: &str,
298 budget: &mut DecodeBudget,
299 depth: usize,
300) -> Result<Vec<Expr>> {
301 let items = required_array(codec, value, field)?;
302 budget.check_collection_len(codec, items.len())?;
303 items
304 .iter()
305 .map(|item| json_to_expr(codec, item, budget, depth))
306 .collect()
307}