Skip to main content

sim_kernel/
shape.rs

1//! The [`Shape`] protocol: the one shared engine for matching and binding.
2//!
3//! Shape is a first-class kernel protocol used across parsing, checking,
4//! binding, dispatch, macro syntax, codec grammar, and overload selection; the
5//! kernel defines the protocol and the match/binding contracts, while concrete
6//! shapes are implemented by the libraries.
7
8use std::{any::Any, collections::BTreeSet, sync::Arc};
9
10use crate::{
11    callable::Callable,
12    env::{Cx, Env},
13    error::{Diagnostic, Error, Result},
14    expr::Expr,
15    hint::{HintMetadata, diagnostic_hints_value},
16    id::{CORE_SHAPE_CLASS_ID, ShapeId, Symbol},
17    object::{Args, ClassRef, Object, RawArgs, ShapeRef},
18    value::Value,
19};
20
21/// The one shared engine for matching and binding across the runtime.
22///
23/// `Shape` is among the kernel's most important contracts: a single protocol
24/// reused for parsing, checking, binding, dispatch, macro syntax, codec
25/// grammar, lambda locals, and overload selection. It is a first-class kernel
26/// protocol -- object-accessible through [`as_shape`](crate::ObjectCompat::as_shape),
27/// callable as a matcher (every `Shape` is a [`Callable`]), and subclassable
28/// through open metadata rather than a closed enum.
29///
30/// The kernel defines only this protocol and the match/binding contracts
31/// ([`ShapeMatch`], [`ShapeBindings`], [`MatchScore`], [`ShapeDoc`]). Concrete
32/// grammars and matchers are supplied by libraries; SIM is a Rust runtime, not
33/// a Lisp runtime, so no particular surface syntax is baked in here.
34///
35/// A type checks a value with [`check_value`](Shape::check_value) and an
36/// expression with [`check_expr`](Shape::check_expr); both yield a
37/// [`ShapeMatch`]. [`describe`](Shape::describe) provides the human-facing
38/// [`ShapeDoc`]. The remaining methods carry optional identity and subshape
39/// metadata and default to neutral answers.
40///
41/// # Examples
42///
43/// ```
44/// use std::sync::Arc;
45/// use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy, Value};
46/// use sim_kernel::shape::{MatchScore, Shape, ShapeDoc, ShapeMatch};
47///
48/// struct AnyShape;
49/// impl Shape for AnyShape {
50///     fn check_value(&self, _cx: &mut Cx, _v: Value) -> sim_kernel::Result<ShapeMatch> {
51///         Ok(ShapeMatch::accept(MatchScore::exact(1)))
52///     }
53///     fn check_expr(&self, _cx: &mut Cx, _e: &sim_kernel::Expr) -> sim_kernel::Result<ShapeMatch> {
54///         Ok(ShapeMatch::accept(MatchScore::exact(1)))
55///     }
56///     fn describe(&self, _cx: &mut Cx) -> sim_kernel::Result<ShapeDoc> {
57///         Ok(ShapeDoc::new("any"))
58///     }
59/// }
60///
61/// let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
62/// let value = cx.factory().string("ok".to_owned()).unwrap();
63/// let matched = AnyShape.check_value(&mut cx, value).unwrap();
64/// assert!(matched.accepted);
65/// ```
66pub trait Shape: Callable {
67    /// Stable [`ShapeId`] when this shape has runtime identity, else `None`.
68    fn id(&self) -> Option<ShapeId> {
69        None
70    }
71
72    /// Symbol naming this shape when it has one, else `None`.
73    fn symbol(&self) -> Option<Symbol> {
74        None
75    }
76
77    /// Parent shapes for the subshape walk; empty by default.
78    fn parents(&self, _cx: &mut Cx) -> Result<Vec<ShapeRef>> {
79        Ok(Vec::new())
80    }
81
82    /// Whether matching this shape may run effects; `false` by default.
83    fn is_effectful(&self) -> bool {
84        false
85    }
86
87    /// Whether this shape accepts every input in its domain; `false` by default.
88    fn is_total(&self) -> bool {
89        false
90    }
91
92    /// Return a concrete implication answer when this shape owns the semantics.
93    ///
94    /// `None` keeps the kernel on the generic id, symbol, Any, and parent walk
95    /// path instead of requiring a closed enum of every concrete shape kind.
96    fn is_subshape_of(&self, _cx: &mut Cx, _parent: &dyn Shape) -> Result<Option<bool>> {
97        Ok(None)
98    }
99
100    /// Check a [`Value`] against this shape, yielding a [`ShapeMatch`].
101    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch>;
102    /// Check an [`Expr`] against this shape, yielding a [`ShapeMatch`].
103    fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch>;
104    /// Produce the human-facing [`ShapeDoc`] for this shape.
105    fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc>;
106}
107
108impl<T> Object for T
109where
110    T: Shape + Any,
111{
112    fn display(&self, cx: &mut Cx) -> Result<String> {
113        let doc = self.describe(cx)?;
114        match self.symbol() {
115            Some(symbol) => Ok(format!("#<shape {} {}>", symbol, doc.name)),
116            None => Ok(format!("#<shape {}>", doc.name)),
117        }
118    }
119
120    fn as_any(&self) -> &dyn Any {
121        self
122    }
123}
124
125impl<T> crate::ObjectCompat for T
126where
127    T: Shape + Any,
128{
129    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
130        let symbol = Symbol::qualified("core", "Shape");
131        if let Some(value) = cx.registry().class_by_symbol(&symbol) {
132            return Ok(value.clone());
133        }
134        cx.factory().class_stub(CORE_SHAPE_CLASS_ID, symbol)
135    }
136    fn as_table(&self, cx: &mut Cx) -> Result<Value> {
137        let doc = self.describe(cx)?;
138        let mut entries = vec![
139            (Symbol::new("name"), cx.factory().string(doc.name)?),
140            (
141                Symbol::new("effectful"),
142                cx.factory().bool(self.is_effectful())?,
143            ),
144            (Symbol::new("total"), cx.factory().bool(self.is_total())?),
145        ];
146        if let Some(symbol) = self.symbol() {
147            entries.push((
148                Symbol::new("symbol"),
149                cx.factory().string(symbol.to_string())?,
150            ));
151        }
152        for (index, detail) in doc.details.into_iter().enumerate() {
153            entries.push((
154                Symbol::qualified("detail", index.to_string()),
155                cx.factory().string(detail)?,
156            ));
157        }
158        cx.factory().table(entries)
159    }
160    fn as_shape(&self) -> Option<&dyn Shape> {
161        Some(self)
162    }
163}
164
165impl<T> Callable for T
166where
167    T: Shape,
168{
169    /// Calls a shape as a matcher over exactly one already-evaluated value.
170    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
171        let [value] = args.values() else {
172            return Err(Error::Eval("shape call expects 1 argument".to_owned()));
173        };
174        call_shape(cx, self, ShapeCallTarget::Value(value.clone()))
175    }
176
177    /// Calls a shape as a matcher over exactly one unevaluated expression.
178    fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
179        let [expr] = args.exprs() else {
180            return Err(Error::Eval("shape call expects 1 expression".to_owned()));
181        };
182        call_shape(cx, self, ShapeCallTarget::Expr(expr.clone()))
183    }
184}
185
186/// Human-facing description of a [`Shape`]: a name plus optional detail lines.
187#[derive(Clone, Debug, Default, PartialEq, Eq)]
188pub struct ShapeDoc {
189    /// Short name of the shape.
190    pub name: String,
191    /// Additional detail lines describing the shape.
192    pub details: Vec<String>,
193}
194
195impl ShapeDoc {
196    /// Create a [`ShapeDoc`] with the given name and no details.
197    pub fn new(name: impl Into<String>) -> Self {
198        Self {
199            name: name.into(),
200            details: Vec::new(),
201        }
202    }
203
204    /// Append a detail line, returning the updated doc (builder style).
205    pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
206        self.details.push(detail.into());
207        self
208    }
209}
210
211/// A match quality score used to rank shapes during overload selection.
212///
213/// Higher scores are preferred; [`reject`](MatchScore::reject) marks a
214/// non-match.
215#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
216pub struct MatchScore(i32);
217
218impl MatchScore {
219    /// Build a score from an explicit integer weight.
220    pub fn exact(value: i32) -> Self {
221        Self(value)
222    }
223
224    /// The score that marks a rejected match (far below any accept).
225    pub fn reject() -> Self {
226        Self(i32::MIN / 2)
227    }
228
229    /// The underlying integer weight.
230    pub fn value(self) -> i32 {
231        self.0
232    }
233}
234
235impl core::ops::AddAssign for MatchScore {
236    fn add_assign(&mut self, rhs: Self) {
237        self.0 = self.0.saturating_add(rhs.0);
238    }
239}
240
241/// Captures produced by a successful match: named values and named exprs.
242///
243/// A match binds names to either runtime [`Value`]s or unevaluated [`Expr`]s;
244/// the bindings can later be projected into an [`Env`].
245#[derive(Clone, Debug, Default)]
246pub struct ShapeBindings {
247    values: Vec<(Symbol, Value)>,
248    exprs: Vec<(Symbol, Expr)>,
249}
250
251impl ShapeBindings {
252    /// Create an empty set of bindings.
253    pub fn new() -> Self {
254        Self::default()
255    }
256
257    /// Bind a name to a runtime [`Value`].
258    pub fn bind_value(&mut self, name: Symbol, value: Value) {
259        self.values.push((name, value));
260    }
261
262    /// Bind a name to an unevaluated [`Expr`].
263    pub fn bind_expr(&mut self, name: Symbol, expr: Expr) {
264        self.exprs.push((name, expr));
265    }
266
267    /// Append all bindings from `other` into this set.
268    pub fn extend(&mut self, other: ShapeBindings) {
269        self.values.extend(other.values);
270        self.exprs.extend(other.exprs);
271    }
272
273    /// The value bindings, in insertion order.
274    pub fn values(&self) -> &[(Symbol, Value)] {
275        &self.values
276    }
277
278    /// The expr bindings, in insertion order.
279    pub fn exprs(&self) -> &[(Symbol, Expr)] {
280        &self.exprs
281    }
282
283    /// Install these bindings as a fresh child of the context's current env.
284    pub fn into_env(self, cx: &mut Cx) -> Result<()> {
285        let env = self.into_child_env(cx)?;
286        *cx.env_mut() = env;
287        Ok(())
288    }
289
290    /// Build a child [`Env`] from the context's env populated with these
291    /// bindings, without installing it.
292    pub fn into_child_env(self, cx: &mut Cx) -> Result<Env> {
293        let mut env = Env::child(Arc::new(cx.env().clone()));
294        for (name, value) in self.values {
295            env.define(name, value);
296        }
297        for (name, expr) in self.exprs {
298            let value = cx.factory().expr(expr)?;
299            env.define(name, value);
300        }
301        Ok(env)
302    }
303}
304
305/// The outcome of checking a value or expr against a [`Shape`].
306///
307/// Carries acceptance, captured [`ShapeBindings`], a [`MatchScore`] for
308/// ranking, and any [`Diagnostic`]s gathered during the check.
309///
310/// # Examples
311///
312/// ```
313/// use sim_kernel::shape::{MatchScore, ShapeMatch};
314///
315/// let ok = ShapeMatch::accept(MatchScore::exact(3));
316/// assert!(ok.accepted);
317/// assert_eq!(ok.score.value(), 3);
318///
319/// let no = ShapeMatch::reject("expected a string");
320/// assert!(!no.accepted);
321/// assert_eq!(no.diagnostics.len(), 1);
322/// ```
323#[derive(Clone, Debug)]
324pub struct ShapeMatch {
325    /// Whether the input satisfied the shape.
326    pub accepted: bool,
327    /// Names captured by the match.
328    pub captures: ShapeBindings,
329    /// Ranking score for overload selection.
330    pub score: MatchScore,
331    /// Diagnostics gathered while matching.
332    pub diagnostics: Vec<Diagnostic>,
333}
334
335impl ShapeMatch {
336    /// An accepted match with the given score and no captures or diagnostics.
337    pub fn accept(score: MatchScore) -> Self {
338        Self {
339            accepted: true,
340            captures: ShapeBindings::new(),
341            score,
342            diagnostics: Vec::new(),
343        }
344    }
345
346    /// A rejected match carrying a single error diagnostic.
347    pub fn reject(message: impl Into<String>) -> Self {
348        Self {
349            accepted: false,
350            captures: ShapeBindings::new(),
351            score: MatchScore::reject(),
352            diagnostics: vec![Diagnostic::error(message)],
353        }
354    }
355
356    /// A rejected match carrying one already-built diagnostic.
357    pub fn reject_with_diagnostic(diagnostic: Diagnostic) -> Self {
358        Self {
359            accepted: false,
360            captures: ShapeBindings::new(),
361            score: MatchScore::reject(),
362            diagnostics: vec![diagnostic],
363        }
364    }
365}
366
367// sim-non-citizen(reason = "shape match result projection; canonical data is exposed as a table", kind = "marker", descriptor = "")
368/// Object wrapper exposing a [`ShapeMatch`] to the runtime as a table.
369#[derive(Clone, Debug)]
370pub struct ShapeMatchObject {
371    matched: ShapeMatch,
372}
373
374impl ShapeMatchObject {
375    /// Wrap a [`ShapeMatch`] as a runtime object.
376    pub fn new(matched: ShapeMatch) -> Self {
377        Self { matched }
378    }
379
380    /// Borrow the wrapped [`ShapeMatch`].
381    pub fn matched(&self) -> &ShapeMatch {
382        &self.matched
383    }
384}
385
386impl Object for ShapeMatchObject {
387    fn display(&self, _cx: &mut Cx) -> Result<String> {
388        Ok(format!(
389            "#<shape-match {} score={}>",
390            if self.matched.accepted {
391                "accepted"
392            } else {
393                "rejected"
394            },
395            self.matched.score.value()
396        ))
397    }
398
399    fn as_any(&self) -> &dyn Any {
400        self
401    }
402}
403
404impl crate::ObjectCompat for ShapeMatchObject {
405    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
406        let symbol = Symbol::qualified("core", "ShapeMatch");
407        if let Some(value) = cx.registry().class_by_symbol(&symbol) {
408            return Ok(value.clone());
409        }
410        cx.factory()
411            .class_stub(crate::id::CORE_SHAPE_MATCH_CLASS_ID, symbol)
412    }
413    fn truth(&self, _cx: &mut Cx) -> Result<bool> {
414        Ok(self.matched.accepted)
415    }
416    fn as_table(&self, cx: &mut Cx) -> Result<Value> {
417        shape_match_table(cx, &self.matched)
418    }
419    fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
420        self.as_table(cx)?.object().as_expr(cx)
421    }
422}
423
424/// A coarse classifier over [`Expr`] variants, used by grammar shapes.
425///
426/// Each variant names a structural kind of expression; [`matches`](ExprKind::matches)
427/// tests an [`Expr`] against the kind and [`name`](ExprKind::name) gives its
428/// stable lowercase tag.
429#[derive(Clone, Debug, PartialEq, Eq)]
430pub enum ExprKind {
431    /// The nil expression.
432    Nil,
433    /// A boolean literal.
434    Bool,
435    /// A number literal.
436    Number,
437    /// A symbol.
438    Symbol,
439    /// A string literal.
440    String,
441    /// A byte-string literal.
442    Bytes,
443    /// A list form.
444    List,
445    /// A vector form.
446    Vector,
447    /// A map form.
448    Map,
449    /// A set form.
450    Set,
451    /// A call form.
452    Call,
453    /// An infix operator form.
454    Infix,
455    /// A prefix operator form.
456    Prefix,
457    /// A postfix operator form.
458    Postfix,
459    /// A block form.
460    Block,
461    /// A quote form.
462    Quote,
463    /// An annotated form.
464    Annotated,
465    /// An extension form.
466    Extension,
467}
468
469impl ExprKind {
470    /// Whether `expr` is an instance of this structural kind.
471    pub fn matches(&self, expr: &Expr) -> bool {
472        matches!(
473            (self, expr),
474            (Self::Nil, Expr::Nil)
475                | (Self::Bool, Expr::Bool(_))
476                | (Self::Number, Expr::Number(_))
477                | (Self::Symbol, Expr::Symbol(_))
478                | (Self::String, Expr::String(_))
479                | (Self::Bytes, Expr::Bytes(_))
480                | (Self::List, Expr::List(_))
481                | (Self::Vector, Expr::Vector(_))
482                | (Self::Map, Expr::Map(_))
483                | (Self::Set, Expr::Set(_))
484                | (Self::Call, Expr::Call { .. })
485                | (Self::Infix, Expr::Infix { .. })
486                | (Self::Prefix, Expr::Prefix { .. })
487                | (Self::Postfix, Expr::Postfix { .. })
488                | (Self::Block, Expr::Block(_))
489                | (Self::Quote, Expr::Quote { .. })
490                | (Self::Annotated, Expr::Annotated { .. })
491                | (Self::Extension, Expr::Extension { .. })
492        )
493    }
494
495    /// The stable lowercase tag for this kind (e.g. `"string"`).
496    pub fn name(&self) -> &'static str {
497        match self {
498            Self::Nil => "nil",
499            Self::Bool => "bool",
500            Self::Number => "number",
501            Self::Symbol => "symbol",
502            Self::String => "string",
503            Self::Bytes => "bytes",
504            Self::List => "list",
505            Self::Vector => "vector",
506            Self::Map => "map",
507            Self::Set => "set",
508            Self::Call => "call",
509            Self::Infix => "infix",
510            Self::Prefix => "prefix",
511            Self::Postfix => "postfix",
512            Self::Block => "block",
513            Self::Quote => "quote",
514            Self::Annotated => "annotated",
515            Self::Extension => "extension",
516        }
517    }
518}
519
520/// What a [`call_shape`] invocation checks: a runtime value or an expr.
521#[derive(Clone, Debug)]
522pub enum ShapeCallTarget {
523    /// Check a runtime [`Value`].
524    Value(Value),
525    /// Check an [`Expr`].
526    Expr(Expr),
527}
528
529/// Run a shape against a [`ShapeCallTarget`] and return the match as a value.
530///
531/// This is the matcher-call path: it dispatches to
532/// [`check_value`](Shape::check_value) or [`check_expr`](Shape::check_expr) and
533/// wraps the [`ShapeMatch`] via [`shape_match_value`].
534pub fn call_shape(cx: &mut Cx, shape: &dyn Shape, target: ShapeCallTarget) -> Result<Value> {
535    let matched = match target {
536        ShapeCallTarget::Value(value) => shape.check_value(cx, value)?,
537        ShapeCallTarget::Expr(expr) => shape.check_expr(cx, &expr)?,
538    };
539    shape_match_value(cx, matched)
540}
541
542/// Wrap a [`ShapeMatch`] as an opaque [`ShapeMatchObject`] runtime value.
543pub fn shape_match_value(cx: &mut Cx, matched: ShapeMatch) -> Result<Value> {
544    cx.factory()
545        .opaque(Arc::new(ShapeMatchObject::new(matched)))
546}
547
548/// Decide whether `child` is a subshape of `parent`.
549///
550/// The generic walk compares ids and symbols, consults the shape's own
551/// [`is_subshape_of`](Shape::is_subshape_of) override, treats the core `Any`
552/// and `AnyShape` symbols as a top for non-effectful shapes, and otherwise
553/// recurses through the declared [`parents`](Shape::parents).
554pub fn shape_is_subshape_of(cx: &mut Cx, child: &dyn Shape, parent: &dyn Shape) -> Result<bool> {
555    let mut seen = BTreeSet::new();
556    shape_is_subshape_of_inner(cx, child, parent, &mut seen)
557}
558
559fn shape_is_subshape_of_inner(
560    cx: &mut Cx,
561    child: &dyn Shape,
562    parent: &dyn Shape,
563    seen: &mut BTreeSet<ShapeIdentity>,
564) -> Result<bool> {
565    if let (Some(child_id), Some(parent_id)) = (child.id(), parent.id())
566        && child_id == parent_id
567    {
568        return Ok(true);
569    }
570    if let (Some(child_symbol), Some(parent_symbol)) = (child.symbol(), parent.symbol())
571        && child_symbol == parent_symbol
572    {
573        return Ok(true);
574    }
575    if let Some(answer) = child.is_subshape_of(cx, parent)? {
576        return Ok(answer);
577    }
578    if !seen.insert(shape_identity(child)) {
579        return Ok(false);
580    }
581    if matches!(
582        parent.symbol(),
583        Some(symbol)
584            if symbol == Symbol::qualified("core", "Any")
585                || symbol == Symbol::qualified("core", "AnyShape")
586    ) && !child.is_effectful()
587    {
588        return Ok(true);
589    }
590    for candidate in child.parents(cx)? {
591        let Some(candidate) = candidate.object().as_shape() else {
592            continue;
593        };
594        if shape_is_subshape_of_inner(cx, candidate, parent, seen)? {
595            return Ok(true);
596        }
597    }
598    Ok(false)
599}
600
601#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
602enum ShapeIdentity {
603    Id(ShapeId),
604    Symbol(Symbol),
605    Pointer(usize),
606}
607
608fn shape_identity(shape: &dyn Shape) -> ShapeIdentity {
609    if let Some(id) = shape.id() {
610        return ShapeIdentity::Id(id);
611    }
612    if let Some(symbol) = shape.symbol() {
613        return ShapeIdentity::Symbol(symbol);
614    }
615    ShapeIdentity::Pointer(shape as *const dyn Shape as *const () as usize)
616}
617
618fn shape_match_table(cx: &mut Cx, matched: &ShapeMatch) -> Result<Value> {
619    let value_captures = cx.factory().table(matched.captures.values().to_vec())?;
620    let expr_captures = cx.factory().table(
621        matched
622            .captures
623            .exprs()
624            .iter()
625            .map(|(symbol, expr)| Ok((symbol.clone(), cx.factory().expr(expr.clone())?)))
626            .collect::<Result<Vec<_>>>()?,
627    )?;
628    let diagnostics = matched
629        .diagnostics
630        .clone()
631        .into_iter()
632        .map(|diagnostic| diagnostic_value(cx, diagnostic))
633        .collect::<Result<Vec<_>>>()?;
634    let diagnostics = cx.factory().list(diagnostics)?;
635    cx.factory().table(vec![
636        (
637            Symbol::new("accepted"),
638            cx.factory().bool(matched.accepted)?,
639        ),
640        (
641            Symbol::new("score"),
642            cx.factory().number_literal(
643                Symbol::qualified("numbers", "f64"),
644                matched.score.value().to_string(),
645            )?,
646        ),
647        (Symbol::qualified("captures", "value"), value_captures),
648        (Symbol::qualified("captures", "expr"), expr_captures),
649        (Symbol::new("diagnostics"), diagnostics),
650    ])
651}
652
653fn diagnostic_value(cx: &mut Cx, diagnostic: Diagnostic) -> Result<Value> {
654    let hints = diagnostic_hints_value(cx, &diagnostic)?;
655    let severity = match diagnostic.severity {
656        crate::error::Severity::Error => "error",
657        crate::error::Severity::Warning => "warning",
658        crate::error::Severity::Info => "info",
659        crate::error::Severity::Note => "note",
660    };
661    let related = diagnostic
662        .related
663        .into_iter()
664        .filter(|related| !HintMetadata::is_hint_diagnostic(related))
665        .map(|related| diagnostic_value(cx, related))
666        .collect::<Result<Vec<_>>>()?;
667    let related = cx.factory().list(related)?;
668    let mut entries = vec![
669        (
670            Symbol::new("severity"),
671            cx.factory().symbol(Symbol::new(severity))?,
672        ),
673        (
674            Symbol::new("message"),
675            cx.factory().string(diagnostic.message)?,
676        ),
677        (Symbol::new("related"), related),
678        (Symbol::new("hints"), hints),
679    ];
680    if let Some(code) = diagnostic.code {
681        entries.push((Symbol::new("code"), cx.factory().symbol(code)?));
682    }
683    cx.factory().table(entries)
684}
685
686#[cfg(test)]
687#[path = "shape_tests.rs"]
688mod tests;