1use std::sync::Arc;
6
7use sim_citizen::{CitizenField, field_error, value_from_expr};
8use sim_kernel::{
9 Cx, Error, Expr, ObjectEncode, ObjectEncoding, Result, Shape, Symbol, Value, force_list_to_vec,
10};
11
12use crate::{
13 AndShape, AnyShape, ClassShape, ExactExprShape, ExprKind, ExprKindShape, HookedShape,
14 ListShape, MatchHook, MatchHookObject, NotShape, OrShape, OrStrategy, RepeatShape, ShapeObject,
15 TableExtraPolicy, TableFieldSpec, TableShape, VennShapeSet, hook_ref_arc,
16 shape_value_with_encoding,
17};
18
19use super::{
20 and_shape_class_symbol, any_shape_class_symbol, class_shape_class_symbol,
21 exact_expr_shape_class_symbol, expr_kind_shape_class_symbol, hooked_shape_class_symbol,
22 list_shape_class_symbol, not_shape_class_symbol, or_shape_class_symbol,
23 repeat_shape_class_symbol, table_shape_class_symbol, venn_shape_set_class_symbol,
24};
25
26impl ObjectEncode for VennShapeSet {
27 fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
28 Ok(constructor_encoding(
29 venn_shape_set_class_symbol(),
30 vec![encode_venn_members(self.members())?],
31 ))
32 }
33}
34
35impl sim_citizen::Citizen for VennShapeSet {
36 fn citizen_symbol() -> Symbol {
37 venn_shape_set_class_symbol()
38 }
39
40 fn citizen_version() -> u32 {
41 1
42 }
43
44 fn citizen_arity() -> usize {
45 1
46 }
47
48 fn citizen_fields() -> &'static [&'static str] {
49 &["members"]
50 }
51}
52
53pub(crate) fn build_shape_value(symbol: Symbol, shape: Arc<dyn Shape>, fields: Vec<Expr>) -> Value {
54 shape_value_with_encoding(symbol.clone(), shape, constructor_encoding(symbol, fields))
55}
56
57pub(crate) fn constructor_encoding(class: Symbol, fields: Vec<Expr>) -> ObjectEncoding {
58 ObjectEncoding::Constructor {
59 class,
60 args: constructor_args(fields),
61 }
62}
63
64fn constructor_expr(class: Symbol, fields: Vec<Expr>) -> Expr {
65 Expr::Call {
66 operator: Box::new(Expr::Symbol(class)),
67 args: constructor_args(fields),
68 }
69}
70
71fn constructor_args(fields: Vec<Expr>) -> Vec<Expr> {
72 let mut args = Vec::with_capacity(fields.len() + 1);
73 args.push(Expr::Symbol(Symbol::new("v1")));
74 args.extend(fields);
75 args
76}
77
78pub(crate) fn int_expr(value: impl ToString) -> Expr {
79 Expr::Number(sim_kernel::NumberLiteral {
80 domain: Symbol::qualified("citizen", "int"),
81 canonical: value.to_string(),
82 })
83}
84
85pub(crate) fn decode_symbol(cx: &mut Cx, value: Value, field: &'static str) -> Result<Symbol> {
86 match value.object().as_expr(cx)? {
87 Expr::Symbol(symbol) => Ok(symbol),
88 Expr::String(text) => Ok(Symbol::new(text)),
89 other => Err(field_error(
90 field,
91 format!("expected symbol or string, found {other:?}"),
92 )),
93 }
94}
95
96pub(crate) fn expr_kind_symbol(kind: &ExprKind) -> Symbol {
97 Symbol::new(kind.name())
98}
99
100pub(crate) fn decode_expr_kind(cx: &mut Cx, value: Value) -> Result<ExprKind> {
101 let symbol = decode_symbol(cx, value, "kind")?;
102 match symbol.name.as_ref() {
103 "nil" => Ok(ExprKind::Nil),
104 "bool" => Ok(ExprKind::Bool),
105 "number" => Ok(ExprKind::Number),
106 "symbol" => Ok(ExprKind::Symbol),
107 "string" => Ok(ExprKind::String),
108 "bytes" => Ok(ExprKind::Bytes),
109 "list" => Ok(ExprKind::List),
110 "vector" => Ok(ExprKind::Vector),
111 "map" => Ok(ExprKind::Map),
112 "set" => Ok(ExprKind::Set),
113 "call" => Ok(ExprKind::Call),
114 "infix" => Ok(ExprKind::Infix),
115 "prefix" => Ok(ExprKind::Prefix),
116 "postfix" => Ok(ExprKind::Postfix),
117 "block" => Ok(ExprKind::Block),
118 "quote" => Ok(ExprKind::Quote),
119 "annotated" => Ok(ExprKind::Annotated),
120 "extension" => Ok(ExprKind::Extension),
121 other => Err(field_error("kind", format!("unknown expr kind {other}"))),
122 }
123}
124
125pub(crate) fn decode_shape_value(
126 cx: &mut Cx,
127 value: Value,
128 field: &'static str,
129) -> Result<Arc<dyn Shape>> {
130 if let Some(shape) = value.object().downcast_ref::<ShapeObject>() {
131 return Ok(shape.shape.clone());
132 }
133 if let Some(shape) = value.object().as_shape() {
134 return clone_supported_shape(shape, field);
135 }
136 let expr = value.object().as_expr(cx)?;
137 let constructed = construct_from_expr(cx, &expr, field)?;
138 constructed
139 .object()
140 .downcast_ref::<ShapeObject>()
141 .map(|shape| shape.shape.clone())
142 .ok_or_else(|| field_error(field, "constructor did not produce a shape value"))
143}
144
145fn clone_supported_shape(shape: &dyn Shape, field: &'static str) -> Result<Arc<dyn Shape>> {
146 if shape.as_any().is::<AnyShape>() {
147 return Ok(Arc::new(AnyShape));
148 }
149 if let Some(exact) = shape.as_any().downcast_ref::<ExactExprShape>() {
150 return Ok(Arc::new(ExactExprShape::new(exact.expected().clone())));
151 }
152 if let Some(kind) = shape.as_any().downcast_ref::<ExprKindShape>() {
153 return Ok(Arc::new(ExprKindShape::new(kind.kind().clone())));
154 }
155 if let Some(class) = shape.as_any().downcast_ref::<ClassShape>() {
156 return Ok(Arc::new(ClassShape::new(class.symbol().clone())));
157 }
158 if let Some(list) = shape.as_any().downcast_ref::<ListShape>() {
159 let items = list
160 .items()
161 .iter()
162 .map(|item| clone_supported_shape(item.as_ref(), field))
163 .collect::<Result<Vec<_>>>()?;
164 return Ok(match list.rest() {
165 Some(rest) => Arc::new(ListShape::with_rest(
166 items,
167 clone_supported_shape(rest.as_ref(), field)?,
168 )),
169 None => Arc::new(ListShape::new(items)),
170 });
171 }
172 Err(field_error(
173 field,
174 "shape value is not one of the citizen-supported pure descriptors",
175 ))
176}
177
178fn construct_from_expr(cx: &mut Cx, expr: &Expr, field: &'static str) -> Result<Value> {
179 let (class, args) = match expr {
180 Expr::Call { operator, args } => match operator.as_ref() {
181 Expr::Symbol(class) => (class.clone(), args.as_slice()),
182 _ => return Err(field_error(field, "constructor operator must be a symbol")),
183 },
184 Expr::List(items) => match items.split_first() {
185 Some((Expr::Symbol(class), args)) => (class.clone(), args),
186 _ => {
187 return Err(field_error(
188 field,
189 "constructor list must start with a symbol",
190 ));
191 }
192 },
193 _ => return Err(field_error(field, "expected constructor expression")),
194 };
195 let values = args
196 .iter()
197 .map(|arg| value_from_expr(cx, arg))
198 .collect::<Result<Vec<_>>>()?;
199 cx.read_construct(&class, values)
200}
201
202pub(crate) fn encode_shape_expr(shape: &dyn Shape) -> Result<Expr> {
203 if shape.as_any().is::<AnyShape>() {
204 return Ok(constructor_expr(any_shape_class_symbol(), Vec::new()));
205 }
206 if let Some(exact) = shape.as_any().downcast_ref::<ExactExprShape>() {
207 return Ok(constructor_expr(
208 exact_expr_shape_class_symbol(),
209 vec![exact.expected().clone()],
210 ));
211 }
212 if let Some(kind) = shape.as_any().downcast_ref::<ExprKindShape>() {
213 return Ok(constructor_expr(
214 expr_kind_shape_class_symbol(),
215 vec![Expr::Symbol(expr_kind_symbol(kind.kind()))],
216 ));
217 }
218 if let Some(class) = shape.as_any().downcast_ref::<ClassShape>() {
219 return Ok(constructor_expr(
220 class_shape_class_symbol(),
221 vec![Expr::Symbol(class.symbol().clone())],
222 ));
223 }
224 if let Some(list) = shape.as_any().downcast_ref::<ListShape>() {
225 return Ok(constructor_expr(
226 list_shape_class_symbol(),
227 vec![
228 encode_shape_list(list.items())?,
229 list.rest()
230 .map(|shape| encode_shape_expr(shape.as_ref()))
231 .transpose()?
232 .unwrap_or(Expr::Nil),
233 ],
234 ));
235 }
236 if let Some(table) = shape.as_any().downcast_ref::<TableShape>() {
237 return Ok(constructor_expr(
238 table_shape_class_symbol(),
239 vec![
240 encode_table_fields(table.fields())?,
241 encode_extra(table.extra())?,
242 ],
243 ));
244 }
245 if let Some(or) = shape.as_any().downcast_ref::<OrShape>() {
246 return Ok(constructor_expr(
247 or_shape_class_symbol(),
248 vec![
249 encode_shape_list(or.choices())?,
250 Expr::Symbol(or_strategy_symbol(or.strategy())),
251 ],
252 ));
253 }
254 if let Some(and) = shape.as_any().downcast_ref::<AndShape>() {
255 return Ok(constructor_expr(
256 and_shape_class_symbol(),
257 vec![encode_shape_list(and.parts())?],
258 ));
259 }
260 if let Some(not) = shape.as_any().downcast_ref::<NotShape>() {
261 return Ok(constructor_expr(
262 not_shape_class_symbol(),
263 vec![encode_shape_expr(not.inner().as_ref())?],
264 ));
265 }
266 if let Some(repeat) = shape.as_any().downcast_ref::<RepeatShape>() {
267 return Ok(constructor_expr(
268 repeat_shape_class_symbol(),
269 vec![
270 encode_shape_expr(repeat.body().as_ref())?,
271 int_expr(repeat.min()),
272 repeat.max().map(int_expr).unwrap_or(Expr::Nil),
273 ],
274 ));
275 }
276 if let Some(hooked) = shape.as_any().downcast_ref::<HookedShape>() {
277 return Ok(constructor_expr(
278 hooked_shape_class_symbol(),
279 vec![
280 encode_shape_expr(hooked.inner().as_ref())?,
281 encode_hooks(hooked.hooks())?,
282 ],
283 ));
284 }
285 Err(Error::Eval(
286 "shape is not a citizen-supported pure descriptor".to_owned(),
287 ))
288}
289
290pub(crate) fn encode_shape_list(shapes: &[Arc<dyn Shape>]) -> Result<Expr> {
291 Ok(Expr::List(
292 shapes
293 .iter()
294 .map(|shape| encode_shape_expr(shape.as_ref()))
295 .collect::<Result<Vec<_>>>()?,
296 ))
297}
298
299pub(crate) fn decode_shape_list(
300 cx: &mut Cx,
301 value: Value,
302 field: &'static str,
303) -> Result<Vec<Arc<dyn Shape>>> {
304 let list = value
305 .object()
306 .as_list()
307 .ok_or_else(|| field_error(field, "expected list of shape constructor descriptors"))?;
308 force_list_to_vec(cx, list, field)?
309 .into_iter()
310 .map(|value| decode_shape_value(cx, value, field))
311 .collect()
312}
313
314pub(crate) fn encode_table_fields(fields: &[TableFieldSpec]) -> Result<Expr> {
315 Ok(Expr::List(
316 fields
317 .iter()
318 .map(|field| {
319 Ok(Expr::List(vec![
320 Expr::Symbol(field.key.clone()),
321 Expr::Bool(field.required),
322 encode_shape_expr(field.shape.as_ref())?,
323 ]))
324 })
325 .collect::<Result<Vec<_>>>()?,
326 ))
327}
328
329pub(crate) fn decode_table_fields(cx: &mut Cx, value: Value) -> Result<Vec<TableFieldSpec>> {
330 let list = value
331 .object()
332 .as_list()
333 .ok_or_else(|| field_error("fields", "expected table field list"))?;
334 force_list_to_vec(cx, list, "fields")?
335 .into_iter()
336 .map(|entry| {
337 let parts = entry
338 .object()
339 .as_list()
340 .ok_or_else(|| field_error("fields", "table field must be a list"))?;
341 let parts = force_list_to_vec(cx, parts, "fields")?;
342 let [key, required, shape] = parts.as_slice() else {
343 return Err(field_error(
344 "fields",
345 "table field must have key, required, shape",
346 ));
347 };
348 Ok(TableFieldSpec {
349 key: decode_symbol(cx, key.clone(), "field-key")?,
350 required: bool::decode_field_value(cx, required.clone(), "required")?,
351 shape: decode_shape_value(cx, shape.clone(), "field-shape")?,
352 })
353 })
354 .collect()
355}
356
357pub(crate) fn encode_extra(extra: &TableExtraPolicy) -> Result<Expr> {
358 match extra {
359 TableExtraPolicy::Allow => Ok(Expr::Symbol(Symbol::new("allow"))),
360 TableExtraPolicy::Reject => Ok(Expr::Symbol(Symbol::new("reject"))),
361 TableExtraPolicy::Shape(shape) => Ok(Expr::List(vec![
362 Expr::Symbol(Symbol::new("shape")),
363 encode_shape_expr(shape.as_ref())?,
364 ])),
365 }
366}
367
368pub(crate) fn decode_extra(cx: &mut Cx, value: Value) -> Result<TableExtraPolicy> {
369 match value.object().as_expr(cx)? {
370 Expr::Symbol(symbol) if symbol.name.as_ref() == "allow" => Ok(TableExtraPolicy::Allow),
371 Expr::Symbol(symbol) if symbol.name.as_ref() == "reject" => Ok(TableExtraPolicy::Reject),
372 Expr::List(items) => match items.as_slice() {
373 [Expr::Symbol(head), shape] if head.name.as_ref() == "shape" => {
374 let value = value_from_expr(cx, shape)?;
375 Ok(TableExtraPolicy::Shape(decode_shape_value(
376 cx, value, "extra",
377 )?))
378 }
379 _ => Err(field_error("extra", "expected (shape descriptor)")),
380 },
381 other => Err(field_error(
382 "extra",
383 format!("expected allow, reject, or shape policy, found {other:?}"),
384 )),
385 }
386}
387
388pub(crate) fn or_strategy_symbol(strategy: OrStrategy) -> Symbol {
389 match strategy {
390 OrStrategy::FirstMatch => Symbol::new("first-match"),
391 OrStrategy::BestScore => Symbol::new("best-score"),
392 }
393}
394
395pub(crate) fn decode_or_strategy(cx: &mut Cx, value: Value) -> Result<OrStrategy> {
396 let symbol = decode_symbol(cx, value, "strategy")?;
397 match symbol.name.as_ref() {
398 "first-match" => Ok(OrStrategy::FirstMatch),
399 "best-score" => Ok(OrStrategy::BestScore),
400 other => Err(field_error("strategy", format!("unknown strategy {other}"))),
401 }
402}
403
404pub(crate) fn encode_hooks(hooks: &[Arc<dyn MatchHook>]) -> Result<Expr> {
405 Ok(Expr::List(
406 hooks
407 .iter()
408 .map(|hook| match hook.object_encoding() {
409 Some(ObjectEncoding::Constructor { class, args }) => Ok(Expr::Call {
410 operator: Box::new(Expr::Symbol(class)),
411 args,
412 }),
413 _ => Err(Error::Eval(format!(
414 "shape hook {} is not a pure descriptor citizen",
415 hook.symbol()
416 ))),
417 })
418 .collect::<Result<Vec<_>>>()?,
419 ))
420}
421
422pub(crate) fn decode_hooks(cx: &mut Cx, value: Value) -> Result<Vec<Arc<dyn MatchHook>>> {
423 let list = value
424 .object()
425 .as_list()
426 .ok_or_else(|| field_error("hooks", "expected hook descriptor list"))?;
427 force_list_to_vec(cx, list, "hooks")?
428 .into_iter()
429 .map(|value| {
430 if let Some(hook) = value.object().downcast_ref::<MatchHookObject>() {
431 return Ok(hook.hook());
432 }
433 let expr = value.object().as_expr(cx)?;
434 let constructed = construct_from_expr(cx, &expr, "hooks")?;
435 hook_ref_arc(&constructed)
436 })
437 .collect()
438}
439
440pub(crate) fn encode_venn_members(members: &[(Symbol, Arc<dyn Shape>)]) -> Result<Expr> {
441 Ok(Expr::List(
442 members
443 .iter()
444 .map(|(name, shape)| {
445 Ok(Expr::List(vec![
446 Expr::Symbol(name.clone()),
447 encode_shape_expr(shape.as_ref())?,
448 ]))
449 })
450 .collect::<Result<Vec<_>>>()?,
451 ))
452}
453
454pub(crate) fn decode_venn_members(
455 cx: &mut Cx,
456 value: Value,
457) -> Result<Vec<(Symbol, Arc<dyn Shape>)>> {
458 let list = value
459 .object()
460 .as_list()
461 .ok_or_else(|| field_error("members", "expected Venn member list"))?;
462 force_list_to_vec(cx, list, "members")?
463 .into_iter()
464 .map(|entry| {
465 let parts = entry
466 .object()
467 .as_list()
468 .ok_or_else(|| field_error("members", "Venn member must be a list"))?;
469 let parts = force_list_to_vec(cx, parts, "members")?;
470 let [name, shape] = parts.as_slice() else {
471 return Err(field_error(
472 "members",
473 "Venn member must have name and shape",
474 ));
475 };
476 Ok((
477 decode_symbol(cx, name.clone(), "member-name")?,
478 decode_shape_value(cx, shape.clone(), "member-shape")?,
479 ))
480 })
481 .collect()
482}