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