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
168impl TryFrom<Expr> for Term {
169    type Error = Error;
170
171    fn try_from(expr: Expr) -> Result<Self> {
172        Self::lower(expr)
173    }
174}
175
176impl From<Term> for Expr {
177    fn from(term: Term) -> Self {
178        match term {
179            Term::Literal(datum) => Self::from(datum),
180            Term::Ref(reference) => ref_to_expr(reference),
181            Term::Local(symbol) => Self::Local(symbol),
182            Term::Let { name, value, body } => Self::Extension {
183                tag: core_symbol("term-let"),
184                payload: Box::new(Self::Map(vec![
185                    (Self::Symbol(Symbol::new("name")), Self::Symbol(name)),
186                    (Self::Symbol(Symbol::new("value")), Self::from(*value)),
187                    (Self::Symbol(Symbol::new("body")), Self::from(*body)),
188                ])),
189            },
190            Term::Op { op, input } => Self::Extension {
191                tag: core_symbol("term-op"),
192                payload: Box::new(Self::Map(vec![
193                    (
194                        Self::Symbol(Symbol::new("op")),
195                        Self::from(op_key_datum(op)),
196                    ),
197                    (Self::Symbol(Symbol::new("input")), Self::from(*input)),
198                ])),
199            },
200            Term::Call { target, args } => Self::Call {
201                operator: Box::new(Self::from(*target)),
202                args: args.into_iter().map(Self::from).collect(),
203            },
204            Term::Seq(items) => Self::Block(items.into_iter().map(Self::from).collect()),
205            Term::Quote { mode, datum } => Self::Quote {
206                mode,
207                expr: Box::new(Self::from(datum)),
208            },
209            Term::Annotated { term, annotations } => Self::Annotated {
210                expr: Box::new(Self::from(*term)),
211                annotations: annotations
212                    .into_iter()
213                    .map(|(name, datum)| (name, Self::from(datum)))
214                    .collect(),
215            },
216            Term::Extension { tag, payload } => Self::Extension {
217                tag,
218                payload: Box::new(Self::from(payload)),
219            },
220        }
221    }
222}
223
224fn lower_terms(exprs: Vec<Expr>) -> Result<Vec<Term>> {
225    exprs.into_iter().map(Term::lower).collect()
226}
227
228fn data_literal(expr: Expr) -> Result<Term> {
229    Datum::try_from(expr).map(Term::Literal)
230}
231
232fn ref_to_expr(reference: Ref) -> Expr {
233    match reference {
234        Ref::Symbol(symbol) => Expr::Symbol(symbol),
235        other => Expr::from(ref_datum(other)),
236    }
237}
238
239fn ref_datum(reference: Ref) -> Datum {
240    match reference {
241        Ref::Symbol(symbol) => Datum::Node {
242            tag: core_symbol("ref"),
243            fields: vec![
244                (Symbol::new("kind"), Datum::Symbol(core_symbol("symbol"))),
245                (Symbol::new("symbol"), Datum::Symbol(symbol)),
246            ],
247        },
248        Ref::Content(content) => Datum::Node {
249            tag: core_symbol("ref"),
250            fields: vec![
251                (Symbol::new("kind"), Datum::Symbol(core_symbol("content"))),
252                (Symbol::new("content"), content_id_datum(content)),
253            ],
254        },
255        Ref::Handle(handle) => Datum::Node {
256            tag: core_symbol("ref"),
257            fields: vec![
258                (Symbol::new("kind"), Datum::Symbol(core_symbol("handle"))),
259                (Symbol::new("id"), handle_id_datum(handle)),
260            ],
261        },
262        Ref::Coord(coordinate) => coordinate_datum(coordinate),
263    }
264}
265
266fn coordinate_datum(coordinate: Coordinate) -> Datum {
267    Datum::Node {
268        tag: core_symbol("ref"),
269        fields: vec![
270            (Symbol::new("kind"), Datum::Symbol(core_symbol("coord"))),
271            (Symbol::new("space"), Datum::Symbol(coordinate.space)),
272            (Symbol::new("ordinal"), content_id_datum(coordinate.ordinal)),
273        ],
274    }
275}
276
277fn content_id_datum(content: ContentId) -> Datum {
278    Datum::Node {
279        tag: core_symbol("content-id"),
280        fields: vec![
281            (Symbol::new("algorithm"), Datum::Symbol(content.algorithm)),
282            (Symbol::new("bytes"), Datum::Bytes(content.bytes.to_vec())),
283        ],
284    }
285}
286
287fn handle_id_datum(handle: HandleId) -> Datum {
288    Datum::Bytes(handle.0.to_be_bytes().to_vec())
289}
290
291fn op_key_datum(op: OpKey) -> Datum {
292    Datum::Node {
293        tag: core_symbol("op-key"),
294        fields: vec![
295            (Symbol::new("namespace"), Datum::Symbol(op.namespace)),
296            (Symbol::new("name"), Datum::Symbol(op.name)),
297            (
298                Symbol::new("version"),
299                Datum::Number(NumberLiteral {
300                    domain: core_symbol("u16"),
301                    canonical: op.version.to_string(),
302                }),
303            ),
304        ],
305    }
306}
307
308fn core_symbol(name: &str) -> Symbol {
309    Symbol::qualified("core", name)
310}