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, 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}