Skip to main content

sim_shape/citizen/
codec.rs

1//! Codec helpers for shape citizens: encode shapes to constructor expressions
2//! and decode constructor fields (symbols, expr kinds, shape lists, table
3//! fields, extra policies) back into shape values.
4
5use 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}