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