Skip to main content

konjure_lang/catalog/
functions.rs

1//! Documented native functions supplied by the Rust interpreter.
2//!
3//! This module is the single description of the native function surface.  The
4//! checker still owns polymorphic specialization, but concrete function values
5//! are constructed from the signatures below instead of a second name match.
6use crate::{
7    Type,
8    ast::Item,
9    catalog::{BuiltinDefinition, ExampleSpec, MemberDoc},
10    parse,
11};
12use std::{collections::BTreeMap, sync::OnceLock};
13
14/// Documentation and, where applicable, the concrete function-value signature
15/// for one Rust interpreter builtin.
16#[derive(Clone, Copy, Debug)]
17pub struct FunctionDescriptor {
18    /// Unqualified interpreter name.
19    pub name: &'static str,
20    /// Documentation category.
21    pub category: &'static str,
22    /// Source-spelled overload declarations.
23    pub declaration: &'static str,
24    /// Behavioral documentation captured from the Rust doc comment.
25    pub docs: &'static str,
26    /// Public support status.
27    pub status: &'static str,
28    /// Parameter and return documentation.
29    pub members: &'static [MemberDoc],
30    /// Runnable source examples and their exact logs.
31    pub examples: &'static [ExampleSpec],
32    /// Rust API page for this descriptor constant.
33    pub rust_path: &'static str,
34    /// Interpreter implementation and type representation.
35    pub representation: &'static str,
36    /// Whether this declaration is a concrete function value rather than a
37    /// context-specialized intrinsic.
38    pub monomorphic: bool,
39}
40
41impl FunctionDescriptor {
42    fn definition(self) -> BuiltinDefinition {
43        BuiltinDefinition {
44            name: self.name,
45            category: self.category,
46            kind: "func",
47            declaration: self.declaration,
48            docs: self.docs,
49            status: self.status,
50            members: self.members,
51            examples: self.examples,
52            rust_path: self.rust_path,
53            representation: self.representation,
54        }
55    }
56}
57
58macro_rules! native_functions {
59    ($(
60        $(#[doc = $doc:expr])*
61        $constant:ident {
62            name: $name:literal,
63            category: $category:literal,
64            declaration: $declaration:expr,
65            status: $status:literal,
66            details: $details:literal,
67            members: $members:expr,
68            examples: &[$(ExampleSpec {
69                title: $title:literal, description: $example_description:literal,
70                source: $source:literal, output: &[$($output:literal),* $(,)?],
71                ticks: $ticks:literal, presentation: $presentation:literal
72            }),* $(,)?],
73            representation: $representation:literal,
74            monomorphic: $monomorphic:literal,
75        }
76    )*) => {
77        $(
78            $(#[doc = $doc])*
79            #[doc = concat!("\n\n", $details)]
80            #[doc = concat!("\n\n# Signature\n\n```text\nfunc ", $name, $declaration, "\n```\n")]
81            $(#[doc = concat!("\n# ", $title, "\n\n", $example_description, "\n\n```text\n", $source, "\n```\n")])*
82            pub const $constant: FunctionDescriptor = FunctionDescriptor {
83                name: $name,
84                category: $category,
85                declaration: concat!("func ", $name, $declaration),
86                docs: concat!($($doc, "\n"),*, "\n", $details),
87                status: $status,
88                members: $members,
89                examples: &[$(ExampleSpec {
90                    title: $title, description: $example_description, source: $source,
91                    output: &[$($output),*], ticks: $ticks, presentation: $presentation,
92                }),*],
93                rust_path: concat!("/sdk/rust/konjure_lang/catalog/functions/constant.", stringify!($constant), ".html"),
94                representation: $representation,
95                monomorphic: $monomorphic,
96            };
97        )*
98
99        /// Every global native name reserved by the interpreter.
100        ///
101        /// Receiver-owned data and numeric operations live in `methods`; they
102        /// must not remain callable through the global builtin namespace.
103        pub const BUILTIN_NAMES: &[&str] = &[$($name),*];
104        const FUNCTIONS: &[FunctionDescriptor] = &[$($constant),*];
105    };
106}
107
108native_functions! {
109    /// Creates an entity with one record component and returns its opaque identity.
110    SPAWN {
111        name: "spawn", category: "Entities", declaration: "(component: C) -> Entity", status: "runtime", details: " The C placeholder means one concrete type value, never a trait or primitive. Entity creation is bounded and rejected when the machine has no remaining entity capacity.",
112        members: &[MemberDoc { name: "component", docs: "A constructed type value; traits and primitive values are rejected." }, MemberDoc { name: "return", docs: "The new Entity identity." }],
113        examples: &[ExampleSpec { title: "Create an entity", description: "A spawned component receives the first entity identity.", source: "let entity = spawn(Vec3 {});\nprint(entity);", output: &["entity(1)"], ticks: 0, presentation: "snippet" }],
114        representation: "Rust interpreter builtin dispatch; Type is specialized from a concrete type argument.", monomorphic: false,
115    }
116    /// Adds a record component to an existing entity.
117    ADD {
118        name: "add", category: "Entities", declaration: "(entity: Entity, component: C) -> Unit", status: "runtime", details: " C means a concrete type value. Adding a component that the entity already owns, or addressing a missing entity, rejects the current transaction.",
119        members: &[MemberDoc { name: "entity", docs: "An existing entity identity." }, MemberDoc { name: "component", docs: "A type value whose type is not already attached." }, MemberDoc { name: "return", docs: "Unit after the component update is accepted." }],
120        examples: &[ExampleSpec { title: "Add a component", description: "A new Sphere component becomes queryable on the entity.", source: "let entity = spawn(Vec3 {});\nadd(entity, Sphere {});\nprint(has[Sphere](entity));", output: &["true"], ticks: 0, presentation: "snippet" }],
121        representation: "Rust interpreter builtin dispatch; Type is specialized from the concrete component type.", monomorphic: false,
122    }
123    /// Reads a typed component copy from an entity; the entity must have that component.
124    GET {
125        name: "get", category: "Entities", declaration: "[C](entity: Entity) -> C", status: "runtime", details: " C means a concrete type selector. The result is a component copy; update the live entity with set, or use a system binding or `bind[C](entity)` method callback for persistent mutation.",
126        members: &[MemberDoc { name: "entity", docs: "An entity that has the selected concrete type component." }, MemberDoc { name: "return", docs: "A copy of component C." }],
127        examples: &[ExampleSpec { title: "Select a component", description: "The explicit selector fixes the component type for a function value.", source: "type Counter { value: Number = 2; }\nlet entity = spawn(Counter {});\nlet read: func(Entity) -> Counter = get[Counter];\nprint(read(entity).value);", output: &["2"], ticks: 0, presentation: "snippet" }],
128        representation: "Rust interpreter builtin dispatch; C must name a concrete type and is checked during specialization.", monomorphic: false,
129    }
130    /// Replaces an existing record component on an entity.
131    SET {
132        name: "set", category: "Entities", declaration: "(entity: Entity, component: C) -> Unit", status: "runtime", details: " C means a concrete type value. set requires that component to exist already and replaces it atomically; use add for a new component.",
133        members: &[MemberDoc { name: "entity", docs: "An entity that already has the component type." }, MemberDoc { name: "component", docs: "The replacement type value." }, MemberDoc { name: "return", docs: "Unit after the replacement is accepted." }],
134        examples: &[ExampleSpec { title: "Replace a component", description: "set updates an already attached component.", source: "let entity = spawn(Vec3 {});\nset(entity, Vec3 { x: 2 });\nprint(get[Vec3](entity).x);", output: &["2"], ticks: 0, presentation: "snippet" }],
135        representation: "Rust interpreter builtin dispatch; Type is specialized from the concrete component type.", monomorphic: false,
136    }
137    /// Tests whether an entity has the selected concrete component type.
138    HAS {
139        name: "has", category: "Entities", declaration: "[C](entity: Entity) -> Bool", status: "runtime", details: " C means a concrete type selector. The result observes the entity's current component set and rejects an unknown entity rather than inventing absence.",
140        members: &[MemberDoc { name: "entity", docs: "An existing entity identity." }, MemberDoc { name: "return", docs: "True when the entity has component C." }],
141        examples: &[ExampleSpec { title: "Test component presence", description: "The selector is required when the function value would otherwise not identify C.", source: "type Counter {}\nlet entity = spawn(Counter {});\nlet present: func(Entity) -> Bool = has[Counter];\nprint(present(entity));", output: &["true"], ticks: 0, presentation: "snippet" }],
142        representation: "Rust interpreter builtin dispatch; C must name a concrete type and is checked during specialization.", monomorphic: false,
143    }
144    /// Returns entity identities that have the selected type or implement the selected trait.
145    QUERY {
146        name: "query", category: "Entities", declaration: "[C]() -> List[Entity]", status: "runtime", details: " C means a concrete type or trait selector. The returned entity handles are ordered by creation and are a snapshot value; later lifecycle changes do not mutate the returned list.",
147        members: &[MemberDoc { name: "return", docs: "Matching entities in stable creation order." }],
148        examples: &[ExampleSpec { title: "Query matching entities", description: "The selector makes the query function value concrete.", source: "type Counter {}\nspawn(Counter {});\nlet find: func() -> List[Entity] = query[Counter];\nprint(find().len());", output: &["1"], ticks: 0, presentation: "snippet" }],
149        representation: "Rust interpreter builtin dispatch; C is a checked concrete type or trait selector.", monomorphic: false,
150    }
151    /// Schedules an entity for removal when the current initializer, callback, or action completes.
152    DESPAWN {
153        name: "despawn", category: "Entities", declaration: "(entity: Entity) -> Unit", status: "runtime", details: " The entity must exist. Removal is deferred until the enclosing initializer, callback, or action completes so checked work sees a consistent world.",
154        members: &[MemberDoc { name: "entity", docs: "An existing entity identity." }, MemberDoc { name: "return", docs: "Unit; removal is deferred to preserve transactional execution." }],
155        examples: &[ExampleSpec { title: "Schedule removal", description: "The current value remains printable while removal is pending.", source: "let entity = spawn(Vec3 {});\ndespawn(entity);\nprint(entity);", output: &["entity(1)"], ticks: 0, presentation: "snippet" }],
156        representation: "Rust interpreter builtin dispatch; Type is func(Entity) -> Unit.", monomorphic: true,
157    }
158    /// Appends a bounded textual rendering of a value to the machine log without external I/O.
159    PRINT {
160        name: "print", category: "Values", declaration: "(value: T) -> Unit", status: "runtime", details: " Rendering must fit the machine string bound. Logs are a bounded ring buffer: when full, the oldest retained line is discarded, and no external I/O occurs.",
161        members: &[MemberDoc { name: "value", docs: "Any checked value whose rendered log text fits the machine string limit." }, MemberDoc { name: "return", docs: "Unit after the bounded log update." }],
162        examples: &[ExampleSpec { title: "Record a value", description: "print writes the interpreter log.", source: "print(\"hello\");", output: &["hello"], ticks: 0, presentation: "snippet" }],
163        representation: "Rust interpreter builtin dispatch; polymorphic calls and function values are checked from their concrete context.", monomorphic: false,
164    }
165    /// Creates a bounded sequence of exactly representable integer Numbers from start inclusive to end exclusive.
166    RANGE { name: "range", category: "Collections", declaration: "(end: Number) -> Res[List[Number], DataError]\nfunc range(start: Number, end: Number) -> Res[List[Number], DataError]", status: "runtime", details: " Each bound must be finite, integral, and exactly representable from -9007199254740991 through 9007199254740991. End is exclusive; descending bounds produce an empty list. Invalid endpoints return DataError; a host resource limit remains a Diagnostic.", members: &[MemberDoc { name: "start", docs: "Exactly representable finite integer from -9007199254740991 through 9007199254740991; omitted start is zero." }, MemberDoc { name: "end", docs: "Exactly representable finite integer from -9007199254740991 through 9007199254740991; it is exclusive." }, MemberDoc { name: "return", docs: "Ok(List[Number]) for valid endpoints, Err(DataError) for invalid numeric endpoints. A host resource limit rejects execution diagnostically." }], examples: &[ExampleSpec { title: "Range from zero", description: "One bound starts at zero.", source: "print(range(3)?.len());", output: &["3"], ticks: 0, presentation: "snippet" }, ExampleSpec { title: "Range with bounds", description: "The start is inclusive and end is exclusive.", source: "print(range(2, 5)?.len());", output: &["3"], ticks: 0, presentation: "snippet" }], representation: "Rust interpreter builtin dispatch; one- and two-argument Number overloads return Res[List[Number], DataError] for invalid numeric endpoints.", monomorphic: false, }
167    /// Does nothing and returns a successful Unit result; useful as a default zero-argument callback.
168    NOOP { name: "noop", category: "Values", declaration: "() -> Res[Unit, DataError]", status: "runtime", details: " It is a concrete function value, so it can be stored in Button.action without a string callback name or a host effect.", members: &[MemberDoc { name: "return", docs: "Ok(Unit) without a state change." }], examples: &[ExampleSpec { title: "Default action", description: "noop is assignable to a zero-argument recoverable callback.", source: "let action: func() -> Res[Unit, DataError] = noop;\naction()?;", output: &[], ticks: 0, presentation: "snippet" }], representation: "Rust interpreter builtin dispatch; Type is func() -> Res[Unit, DataError].", monomorphic: true, }
169    /// Accepts and ignores one Number; useful as a default slider callback.
170    IGNORE_NUMBER { name: "ignore_number", category: "Values", declaration: "(value: Number) -> Res[Unit, DataError]", status: "runtime", details: " It is a concrete function value for Slider.action. The Number is checked then discarded, with no scene or host state update.", members: &[MemberDoc { name: "value", docs: "A Number accepted without a state change." }, MemberDoc { name: "return", docs: "Ok(Unit)." }], examples: &[ExampleSpec { title: "Default slider action", description: "ignore_number has the Slider callback type.", source: "let action: func(Number) -> Res[Unit, DataError] = ignore_number;\naction(0.5)?;", output: &[], ticks: 0, presentation: "snippet" }], representation: "Rust interpreter builtin dispatch; Type is func(Number) -> Res[Unit, DataError].", monomorphic: true, }
171    /// Binds a concrete entity component so extracted methods can update that live component transactionally.
172    BIND { name: "bind", category: "Entities", declaration: "[C](entity: Entity) -> ComponentReference[C]", status: "runtime", details: " C means a concrete type selector, never a trait. The reference owns an entity and component identity; extracted mutating methods commit back transactionally and fail if the component is unavailable.", members: &[MemberDoc { name: "entity", docs: "An entity with the selected concrete type component." }, MemberDoc { name: "return", docs: "A ComponentReference[C] whose mutating extracted methods write back to that entity." }], examples: &[ExampleSpec { title: "Bind a live component", description: "A bound mutating method commits its component update.", source: "type Counter { value: Number = 0; func advance() -> Res[Unit, DataError] { self.value = (self.value + 1)?; ret Ok(()); } }\nlet entity = spawn(Counter {});\nlet reference = bind[Counter](entity);\nlet advance = reference.advance;\nadvance()?;\nprint(get[Counter](entity).value);", output: &["1"], ticks: 0, presentation: "snippet" }], representation: "Rust interpreter builtin dispatch; C must name a concrete type and produces ComponentReference[C] for typed function values.", monomorphic: false, }
173    /// Constructs packed numeric data with explicit dimensions.
174    TENSOR {
175        name: "tensor", category: "Collections", declaration: "[T](values: List[T], dimensions: List[Number]) -> Res[Tensor[T], DataError]", status: "runtime",
176        details: "T is a numeric dtype. Values are row-major and their count must equal the checked product of the dimensions. Invalid shapes return Err(DataError). Host byte and execution limits reject execution before allocation.",
177        members: &[MemberDoc { name: "values", docs: "Flat elements of the selected dtype; literals inherit T." }, MemberDoc { name: "dimensions", docs: "Nonnegative integer dimensions in row-major axis order; [] constructs a rank-zero scalar from one value." }, MemberDoc { name: "return", docs: "Ok(Tensor[T]) with packed storage, or Err(DataError) for invalid dimensions or an element-count mismatch." }],
178        examples: &[ExampleSpec { title: "Constructs packed numeric data with explicit dimensions", description: "Run the example, then change the data and inspect the result.", source: "let samples = tensor[f32]([1, 2, 3, 4], [2, 2])?;\nprint(samples[1, 0]?);", output: &["3"], ticks: 0, presentation: "snippet" }],
179        representation: "Rust SDK data primitives; the language checker specializes the same runtime operation.", monomorphic: false,
180    }
181    /// Builds immutable bytes from checked unsigned 8-bit values.
182    BYTES {
183        name: "bytes", category: "Values", declaration: "(values: List[u8]) -> Bin", status: "runtime",
184        details: "Each element is a u8, so out-of-range literal bytes are compile errors. The Bin owns shared immutable storage and has no text encoding until decoded explicitly.",
185        members: &[MemberDoc { name: "values", docs: "Unsigned 8-bit values; list literals inherit u8." }, MemberDoc { name: "return", docs: "Immutable packed bytes." }],
186        examples: &[ExampleSpec { title: "Builds immutable bytes from checked unsigned 8-bit values", description: "Run the example, then change the data and inspect the result.", source: "let payload: Bin = bytes([65, 66]);\nprint(payload.decode()?);", output: &["AB"], ticks: 0, presentation: "snippet" }],
187        representation: "Rust SDK data primitives; the language checker specializes the same runtime operation.", monomorphic: true,
188    }
189}
190
191/// Build catalog entries from the same descriptors that reserve native names and
192/// construct concrete function-value signatures.
193pub(crate) fn definitions() -> Vec<BuiltinDefinition> {
194    FUNCTIONS
195        .iter()
196        .copied()
197        .map(FunctionDescriptor::definition)
198        .collect()
199}
200
201/// Returns a concrete builtin function-value signature when the descriptor has one.
202pub(crate) fn monomorphic_signature(name: &str) -> Option<Type> {
203    static SIGNATURES: OnceLock<BTreeMap<&'static str, Type>> = OnceLock::new();
204    SIGNATURES
205        .get_or_init(|| {
206            FUNCTIONS
207                .iter()
208                .filter(|descriptor| descriptor.monomorphic)
209                .map(|descriptor| {
210                    let source = format!("trait Signature {{ {}; }}", descriptor.declaration);
211                    let items = parse(&source, "builtin")
212                        .expect("monomorphic builtin declaration must parse");
213                    let [Item::Trait(contract)] = items.as_slice() else {
214                        panic!("monomorphic builtin declaration must be a trait method");
215                    };
216                    let [function] = contract.methods.as_slice() else {
217                        panic!("monomorphic builtin declaration must have one overload");
218                    };
219                    (
220                        descriptor.name,
221                        Type::function(
222                            function
223                                .parameters
224                                .iter()
225                                .map(|parameter| parameter.ty.clone())
226                                .collect(),
227                            function.returns.clone(),
228                        ),
229                    )
230                })
231                .collect()
232        })
233        .get(name)
234        .cloned()
235}