Skip to main content

sim_kernel/
term.rs

1//! The [`Term`] contract: the checked form that bridges expressions and data.
2//!
3//! The kernel defines the term representation and its operation keys; it sits
4//! on the path from checked forms to objects, and libraries interpret terms.
5
6use crate::{
7    datum::Datum,
8    error::{Error, Result},
9    expr::{Expr, NumberLiteral, QuoteMode},
10    id::Symbol,
11    ref_id::{ContentId, Coordinate, HandleId, Ref},
12};
13
14/// Stable operation identity used by lowered `Term::Op` nodes.
15///
16/// Lowered `Term::Op` nodes carry only the key value so terms can express
17/// explicit op invocations. Operation specs, dispatch, adapters, and
18/// capability gates are resolved by the op layer.
19#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct OpKey {
21    /// The operation's namespace.
22    pub namespace: Symbol,
23    /// The operation's name within the namespace.
24    pub name: Symbol,
25    /// The operation's version.
26    pub version: u16,
27}
28
29impl OpKey {
30    /// Builds an op key from a namespace, name, and version.
31    pub fn new(namespace: Symbol, name: Symbol, version: u16) -> Self {
32        Self {
33            namespace,
34            name,
35            version,
36        }
37    }
38}
39
40/// Lowered executable form.
41///
42/// Current `Expr` values lower as follows:
43///
44/// - scalar data -> `Term::Literal`
45/// - `Expr::Symbol` -> `Term::Ref(Ref::Symbol)`
46/// - `Expr::Local` -> `Term::Local`
47/// - data-only list, vector, map, and set -> `Term::Literal`
48/// - call -> `Term::Call`
49/// - infix, prefix, and postfix -> `Term::Call` with a symbolic target
50/// - block -> `Term::Seq`
51/// - quote -> `Term::Quote` over pure `Datum`
52/// - annotated -> `Term::Annotated` with datum annotations
53/// - extension -> `Term::Extension` with a datum payload
54#[derive(Clone, Debug, PartialEq, Eq, Hash)]
55pub enum Term {
56    /// A constant datum value.
57    Literal(Datum),
58    /// A reference resolved through the registry or substrate.
59    Ref(Ref),
60    /// A lexical local binding referenced by name.
61    Local(Symbol),
62    /// A local binding form: bind `name` to `value` within `body`.
63    Let {
64        /// The bound name.
65        name: Symbol,
66        /// The term producing the bound value.
67        value: Box<Term>,
68        /// The body evaluated with the binding in scope.
69        body: Box<Term>,
70    },
71    /// An explicit operation invocation over one input term.
72    Op {
73        /// The operation key naming the op.
74        op: OpKey,
75        /// The input term the op consumes.
76        input: Box<Term>,
77    },
78    /// A call of `target` with positional `args`.
79    Call {
80        /// The term producing the callable target.
81        target: Box<Term>,
82        /// The positional argument terms.
83        args: Vec<Term>,
84    },
85    /// A sequence of terms evaluated in order.
86    Seq(Vec<Term>),
87    /// A quoted datum carried at a given quote mode.
88    Quote {
89        /// The quote mode (quote, quasiquote, ...).
90        mode: QuoteMode,
91        /// The quoted datum.
92        datum: Datum,
93    },
94    /// A term carrying datum-valued annotations.
95    Annotated {
96        /// The annotated inner term.
97        term: Box<Term>,
98        /// The name/datum annotation pairs.
99        annotations: Vec<(Symbol, Datum)>,
100    },
101    /// An open extension term with a tag and datum payload.
102    Extension {
103        /// The extension tag.
104        tag: Symbol,
105        /// The extension payload datum.
106        payload: Datum,
107    },
108}
109
110impl Term {
111    /// Lowers a checked [`Expr`] into its executable [`Term`] form.
112    ///
113    /// This walks the expression graph and produces the lowered representation
114    /// described on [`Term`]; it fails when a sub-expression cannot become a
115    /// pure [`Datum`].
116    pub fn lower(expr: Expr) -> Result<Self> {
117        match expr {
118            Expr::Nil => Ok(Self::Literal(Datum::Nil)),
119            Expr::Bool(value) => Ok(Self::Literal(Datum::Bool(value))),
120            Expr::Number(value) => Ok(Self::Literal(Datum::Number(value))),
121            Expr::Symbol(symbol) => Ok(Self::Ref(Ref::Symbol(symbol))),
122            Expr::Local(symbol) => Ok(Self::Local(symbol)),
123            Expr::String(value) => Ok(Self::Literal(Datum::String(value))),
124            Expr::Bytes(value) => Ok(Self::Literal(Datum::Bytes(value))),
125            Expr::List(items) => data_literal(Expr::List(items)),
126            Expr::Vector(items) => data_literal(Expr::Vector(items)),
127            Expr::Map(entries) => data_literal(Expr::Map(entries)),
128            Expr::Set(items) => data_literal(Expr::Set(items)),
129            Expr::Call { operator, args } => Ok(Self::Call {
130                target: Box::new(Self::lower(*operator)?),
131                args: lower_terms(args)?,
132            }),
133            Expr::Infix {
134                operator,
135                left,
136                right,
137            } => Ok(Self::Call {
138                target: Box::new(Self::Ref(Ref::Symbol(operator))),
139                args: vec![Self::lower(*left)?, Self::lower(*right)?],
140            }),
141            Expr::Prefix { operator, arg } | Expr::Postfix { operator, arg } => Ok(Self::Call {
142                target: Box::new(Self::Ref(Ref::Symbol(operator))),
143                args: vec![Self::lower(*arg)?],
144            }),
145            Expr::Block(items) => Ok(Self::Seq(lower_terms(items)?)),
146            Expr::Quote { mode, expr } => Ok(Self::Quote {
147                mode,
148                datum: Datum::try_from(*expr)?,
149            }),
150            Expr::Annotated { expr, annotations } => {
151                let annotations = annotations
152                    .into_iter()
153                    .map(|(name, expr)| Ok((name, Datum::try_from(expr)?)))
154                    .collect::<Result<Vec<_>>>()?;
155                Ok(Self::Annotated {
156                    term: Box::new(Self::lower(*expr)?),
157                    annotations,
158                })
159            }
160            Expr::Extension { tag, payload } => Ok(Self::Extension {
161                tag,
162                payload: Datum::try_from(*payload)?,
163            }),
164        }
165    }
166
167    /// Encodes this lowered executable form as canonical SIM data.
168    pub fn to_datum(&self) -> Datum {
169        term_datum(self)
170    }
171}
172
173impl TryFrom<Expr> for Term {
174    type Error = Error;
175
176    fn try_from(expr: Expr) -> Result<Self> {
177        Self::lower(expr)
178    }
179}
180
181impl From<Term> for Datum {
182    fn from(term: Term) -> Self {
183        term.to_datum()
184    }
185}
186
187impl From<Term> for Expr {
188    fn from(term: Term) -> Self {
189        match term {
190            Term::Literal(datum) => Self::from(datum),
191            Term::Ref(reference) => ref_to_expr(reference),
192            Term::Local(symbol) => Self::Local(symbol),
193            Term::Let { name, value, body } => Self::Extension {
194                tag: core_symbol("term-let"),
195                payload: Box::new(Self::Map(vec![
196                    (Self::Symbol(Symbol::new("name")), Self::Symbol(name)),
197                    (Self::Symbol(Symbol::new("value")), Self::from(*value)),
198                    (Self::Symbol(Symbol::new("body")), Self::from(*body)),
199                ])),
200            },
201            Term::Op { op, input } => Self::Extension {
202                tag: core_symbol("term-op"),
203                payload: Box::new(Self::Map(vec![
204                    (
205                        Self::Symbol(Symbol::new("op")),
206                        Self::from(op_key_datum(op)),
207                    ),
208                    (Self::Symbol(Symbol::new("input")), Self::from(*input)),
209                ])),
210            },
211            Term::Call { target, args } => Self::Call {
212                operator: Box::new(Self::from(*target)),
213                args: args.into_iter().map(Self::from).collect(),
214            },
215            Term::Seq(items) => Self::Block(items.into_iter().map(Self::from).collect()),
216            Term::Quote { mode, datum } => Self::Quote {
217                mode,
218                expr: Box::new(Self::from(datum)),
219            },
220            Term::Annotated { term, annotations } => Self::Annotated {
221                expr: Box::new(Self::from(*term)),
222                annotations: annotations
223                    .into_iter()
224                    .map(|(name, datum)| (name, Self::from(datum)))
225                    .collect(),
226            },
227            Term::Extension { tag, payload } => Self::Extension {
228                tag,
229                payload: Box::new(Self::from(payload)),
230            },
231        }
232    }
233}
234
235fn lower_terms(exprs: Vec<Expr>) -> Result<Vec<Term>> {
236    exprs.into_iter().map(Term::lower).collect()
237}
238
239fn data_literal(expr: Expr) -> Result<Term> {
240    Datum::try_from(expr).map(Term::Literal)
241}
242
243fn term_datum(term: &Term) -> Datum {
244    match term {
245        Term::Literal(datum) => term_node("literal", vec![(Symbol::new("datum"), datum.clone())]),
246        Term::Ref(reference) => term_node(
247            "ref",
248            vec![(Symbol::new("ref"), ref_datum(reference.clone()))],
249        ),
250        Term::Local(symbol) => term_node(
251            "local",
252            vec![(Symbol::new("symbol"), Datum::Symbol(symbol.clone()))],
253        ),
254        Term::Let { name, value, body } => term_node(
255            "let",
256            vec![
257                (Symbol::new("name"), Datum::Symbol(name.clone())),
258                (Symbol::new("value"), term_datum(value)),
259                (Symbol::new("body"), term_datum(body)),
260            ],
261        ),
262        Term::Op { op, input } => term_node(
263            "op",
264            vec![
265                (Symbol::new("op"), op_key_datum(op.clone())),
266                (Symbol::new("input"), term_datum(input)),
267            ],
268        ),
269        Term::Call { target, args } => term_node(
270            "call",
271            vec![
272                (Symbol::new("target"), term_datum(target)),
273                (Symbol::new("args"), terms_datum(args)),
274            ],
275        ),
276        Term::Seq(items) => term_node("seq", vec![(Symbol::new("items"), terms_datum(items))]),
277        Term::Quote { mode, datum } => term_node(
278            "quote",
279            vec![
280                (Symbol::new("mode"), Datum::Symbol(quote_mode_symbol(*mode))),
281                (Symbol::new("datum"), datum.clone()),
282            ],
283        ),
284        Term::Annotated { term, annotations } => term_node(
285            "annotated",
286            vec![
287                (Symbol::new("term"), term_datum(term)),
288                (
289                    Symbol::new("annotations"),
290                    Datum::Vector(
291                        annotations
292                            .iter()
293                            .map(|(name, datum)| Datum::Node {
294                                tag: core_symbol("term-annotation"),
295                                fields: vec![
296                                    (Symbol::new("name"), Datum::Symbol(name.clone())),
297                                    (Symbol::new("datum"), datum.clone()),
298                                ],
299                            })
300                            .collect(),
301                    ),
302                ),
303            ],
304        ),
305        Term::Extension { tag, payload } => term_node(
306            "extension",
307            vec![
308                (Symbol::new("tag"), Datum::Symbol(tag.clone())),
309                (Symbol::new("payload"), payload.clone()),
310            ],
311        ),
312    }
313}
314
315fn term_node(kind: &str, fields: Vec<(Symbol, Datum)>) -> Datum {
316    let mut node_fields = vec![(Symbol::new("kind"), Datum::Symbol(core_symbol(kind)))];
317    node_fields.extend(fields);
318    Datum::Node {
319        tag: core_symbol("term"),
320        fields: node_fields,
321    }
322}
323
324fn terms_datum(terms: &[Term]) -> Datum {
325    Datum::Vector(terms.iter().map(term_datum).collect())
326}
327
328fn quote_mode_symbol(mode: QuoteMode) -> Symbol {
329    core_symbol(match mode {
330        QuoteMode::Quote => "quote",
331        QuoteMode::QuasiQuote => "quasiquote",
332        QuoteMode::Unquote => "unquote",
333        QuoteMode::Splice => "splice",
334        QuoteMode::Syntax => "syntax",
335    })
336}
337
338fn ref_to_expr(reference: Ref) -> Expr {
339    match reference {
340        Ref::Symbol(symbol) => Expr::Symbol(symbol),
341        other => Expr::from(ref_datum(other)),
342    }
343}
344
345fn ref_datum(reference: Ref) -> Datum {
346    match reference {
347        Ref::Symbol(symbol) => Datum::Node {
348            tag: core_symbol("ref"),
349            fields: vec![
350                (Symbol::new("kind"), Datum::Symbol(core_symbol("symbol"))),
351                (Symbol::new("symbol"), Datum::Symbol(symbol)),
352            ],
353        },
354        Ref::Content(content) => Datum::Node {
355            tag: core_symbol("ref"),
356            fields: vec![
357                (Symbol::new("kind"), Datum::Symbol(core_symbol("content"))),
358                (Symbol::new("content"), content_id_datum(content)),
359            ],
360        },
361        Ref::Handle(handle) => Datum::Node {
362            tag: core_symbol("ref"),
363            fields: vec![
364                (Symbol::new("kind"), Datum::Symbol(core_symbol("handle"))),
365                (Symbol::new("id"), handle_id_datum(handle)),
366            ],
367        },
368        Ref::Coord(coordinate) => coordinate_datum(coordinate),
369    }
370}
371
372fn coordinate_datum(coordinate: Coordinate) -> Datum {
373    Datum::Node {
374        tag: core_symbol("ref"),
375        fields: vec![
376            (Symbol::new("kind"), Datum::Symbol(core_symbol("coord"))),
377            (Symbol::new("space"), Datum::Symbol(coordinate.space)),
378            (Symbol::new("ordinal"), content_id_datum(coordinate.ordinal)),
379        ],
380    }
381}
382
383fn content_id_datum(content: ContentId) -> Datum {
384    Datum::Node {
385        tag: core_symbol("content-id"),
386        fields: vec![
387            (Symbol::new("algorithm"), Datum::Symbol(content.algorithm)),
388            (Symbol::new("bytes"), Datum::Bytes(content.bytes.to_vec())),
389        ],
390    }
391}
392
393fn handle_id_datum(handle: HandleId) -> Datum {
394    Datum::Bytes(handle.0.to_be_bytes().to_vec())
395}
396
397fn op_key_datum(op: OpKey) -> Datum {
398    Datum::Node {
399        tag: core_symbol("op-key"),
400        fields: vec![
401            (Symbol::new("namespace"), Datum::Symbol(op.namespace)),
402            (Symbol::new("name"), Datum::Symbol(op.name)),
403            (
404                Symbol::new("version"),
405                Datum::Number(NumberLiteral {
406                    domain: core_symbol("u16"),
407                    canonical: op.version.to_string(),
408                }),
409            ),
410        ],
411    }
412}
413
414fn core_symbol(name: &str) -> Symbol {
415    Symbol::qualified("core", name)
416}