Add host prelude embedding API
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Embedding API
|
||||
|
||||
Decodal core can be embedded without giving the core crate access to a filesystem.
|
||||
The host supplies imported sources through `SourceLoader` and may also provide global bindings through the host prelude API.
|
||||
|
||||
## Host prelude
|
||||
|
||||
`Engine` owns a prelude environment.
|
||||
Bindings in this environment are visible from every module loaded by the engine.
|
||||
|
||||
```text
|
||||
prelude env
|
||||
↓
|
||||
module root env
|
||||
↓
|
||||
let / function env
|
||||
```
|
||||
|
||||
Module top-level bindings shadow prelude bindings.
|
||||
Primitive type names such as `String`, `Int`, `Float`, and `Bool` are handled before environment lookup, so they are reserved and cannot be shadowed by host bindings.
|
||||
|
||||
## Global bindings
|
||||
|
||||
The host can bind values before adding or evaluating user sources.
|
||||
|
||||
```rust
|
||||
use decodal_core::{EmptyLoader, Engine, HostValue};
|
||||
|
||||
let mut engine = Engine::new(EmptyLoader);
|
||||
|
||||
engine.bind_global(
|
||||
"Service",
|
||||
HostValue::object([
|
||||
("name", HostValue::string_type()),
|
||||
("port", HostValue::int_type().gt(443).default_int(8443)?),
|
||||
("enabled", HostValue::bool_type().default_bool(true)?),
|
||||
]),
|
||||
)?;
|
||||
```
|
||||
|
||||
A user source can then refer to `Service` without importing it.
|
||||
|
||||
```dcdl
|
||||
Service & {
|
||||
name = "api";
|
||||
port = 9443;
|
||||
}
|
||||
```
|
||||
|
||||
## HostValue
|
||||
|
||||
`HostValue` is the public builder-facing value representation for embedding.
|
||||
It keeps host code from constructing internal `ThunkId` or `ObjectValue` values directly.
|
||||
|
||||
```text
|
||||
HostValue =
|
||||
String
|
||||
Int
|
||||
Float
|
||||
Bool
|
||||
Array(Vec<HostValue>)
|
||||
Object(Vec<HostField>)
|
||||
Abstract { constraints, default }
|
||||
```
|
||||
|
||||
When a host value is bound, the engine internalizes it into `RuntimeValue` and allocates value thunks for object fields, array items, and defaults.
|
||||
|
||||
## Abstract host objects
|
||||
|
||||
A host-provided schema object is represented as a concrete object structure whose fields may contain abstract values.
|
||||
|
||||
```rust
|
||||
HostValue::object([
|
||||
("name", HostValue::string_type()),
|
||||
("port", HostValue::int_type().gt(443).default_int(8443)?),
|
||||
])
|
||||
```
|
||||
|
||||
Conceptually this becomes:
|
||||
|
||||
```text
|
||||
Concrete(Object {
|
||||
name -> Thunk(Abstract { constraints: [String], default: none })
|
||||
port -> Thunk(Abstract { constraints: [Int, > 443], default: 8443 })
|
||||
})
|
||||
```
|
||||
|
||||
This matches the runtime model used for Decodal source-defined schema objects.
|
||||
|
||||
## SourceLoader and prelude together
|
||||
|
||||
`SourceLoader` and host prelude bindings are independent mechanisms.
|
||||
|
||||
- Use `SourceLoader` when user sources should explicitly import host-provided modules.
|
||||
- Use prelude bindings when host-provided schemas or constants should be globally available.
|
||||
|
||||
Both mechanisms share the same runtime evaluator, thunk model, and materialization rules.
|
||||
@@ -15,3 +15,4 @@ bytecode VM や JIT ではなく、AST を demand-driven に評価すること
|
||||
3. [Thunk and Lazy Evaluation](./thunk-and-lazy-evaluation.md)
|
||||
4. [Composition and Materialization](./composition-and-materialization.md)
|
||||
5. [Diagnostics and Fallback](./diagnostics-and-fallback.md)
|
||||
6. [Embedding API](./embedding-api.md)
|
||||
|
||||
Reference in New Issue
Block a user