Refocus manual on public language usage
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
# Embedding
|
||||
|
||||
Decodal runtime は filesystem、network、environment variable を直接読み込まない。
|
||||
ホストは global bindings と import の解決方法を注入し、評価結果を `Data` または application type として受け取る。
|
||||
|
||||
## Rust runtime
|
||||
|
||||
`Engine` は `ImportLoader` と global bindings を持つ。
|
||||
外部 resource を必要としない source には `EmptyLoader` を使える。
|
||||
|
||||
```rust
|
||||
use decodal::{EmptyLoader, Engine, Value};
|
||||
|
||||
let mut engine = Engine::new(EmptyLoader);
|
||||
engine.bind_global(
|
||||
"Service",
|
||||
Value::object([
|
||||
("name", Value::string_type()),
|
||||
("port", Value::int_type().gt(443).default_int(8443)?),
|
||||
("enabled", Value::bool_type().default_bool(true)?),
|
||||
]),
|
||||
)?;
|
||||
|
||||
let module = engine.add_root_source(
|
||||
"service.dcdl",
|
||||
"service.dcdl",
|
||||
r#"{ name = "api"; port = 9443; } as Service"#,
|
||||
)?;
|
||||
let value = engine.eval_module(module)?;
|
||||
let data = engine.materialize(&value)?;
|
||||
```
|
||||
|
||||
`Value` は concrete value と host-defined range の両方を表す公開型である。
|
||||
主な constructor は次の通りである。
|
||||
|
||||
- `Value::string`、`int`、`float`、`bool`、`array`、`object`: concrete value。
|
||||
- `Value::string_type`、`int_type`、`float_type`、`bool_type`: primitive range。
|
||||
- `Value::unknown`: 最上位 range `Unknown`。
|
||||
- `Value::array_of`: 全要素へ同じ range を適用する array range。
|
||||
- `Value::map_of`: 全 field value へ同じ range を適用する associative-array range。
|
||||
- `Value::object_with_rest`: named fields と残りの field range を持つ object。
|
||||
|
||||
## Typed Rust integration
|
||||
|
||||
`derive` feature の `Decodal` derive は、Rust struct から `DecodalSchema` と `DecodalDecode` を生成する。
|
||||
|
||||
```rust
|
||||
use decodal::{Decodal, DecodalDecode, DecodalSchema};
|
||||
|
||||
#[derive(Decodal)]
|
||||
struct Service {
|
||||
name: String,
|
||||
|
||||
#[decodal(gt = 443, default = 8443)]
|
||||
port: i64,
|
||||
|
||||
#[decodal(rename = "feature.enable", default = true)]
|
||||
feature_enabled: bool,
|
||||
}
|
||||
```
|
||||
|
||||
`DecodalSchema::decodal_schema()` は `Engine::bind_global` に渡せる `Value` を返す。
|
||||
materialize 済みの `Data` は `DecodalDecode::decodal_decode` で Rust type に変換できる。
|
||||
|
||||
追加 field を受け取る struct では、map field に `#[decodal(rest)]` を付ける。
|
||||
|
||||
```rust
|
||||
use std::collections::BTreeMap;
|
||||
use decodal::{Data, Decodal};
|
||||
|
||||
#[derive(Decodal)]
|
||||
struct OpenConfig {
|
||||
enabled: bool,
|
||||
|
||||
#[decodal(rest)]
|
||||
extra: BTreeMap<String, Data>,
|
||||
}
|
||||
```
|
||||
|
||||
`BTreeMap<String, Data>` は `...Unknown`、`BTreeMap<String, String>` は `...String` に対応する。
|
||||
named fields は rest map から除外される。
|
||||
`#[decodal(rest)]` は struct ごとに1つだけ指定でき、`rename`、`default`、field constraints とは併用できない。
|
||||
|
||||
## Imports
|
||||
|
||||
`ImportLoader::load` は `LoadedImport::Source` または `LoadedImport::Value` を返す。
|
||||
ホストは specifier の解決、filesystem や他の storage からの読み込み、content type の判定をすべて管理する。
|
||||
|
||||
`LoadedImport::Source` は DCDL source を返す。
|
||||
`LoadedImport::Value` は Markdown、JSON、TOML などをホスト独自の規則で構造化して返す用途に使える。
|
||||
|
||||
```text
|
||||
import "./post.md"
|
||||
-> host reads and parses Markdown
|
||||
-> LoadedImport::Value {
|
||||
key: "content/post.md",
|
||||
value: { frontmatter: {...}, body: "..." }
|
||||
}
|
||||
-> normal Decodal validation and materialization
|
||||
```
|
||||
|
||||
stable `key` は import の同一性と diagnostic の provenance に使われる。
|
||||
構造化した値が Decodal constraint に違反した場合、diagnostic は import key と logical value path、および違反した DCDL constraint の source span を示す。
|
||||
外部形式そのものの parse error と位置情報は loader が報告する。
|
||||
|
||||
## Shared host environment
|
||||
|
||||
`HostEnvironment` は loader の作成と global binding の設定を1つにまとめる。
|
||||
同じ environment を runtime、language service、LSP へ渡すことで、実行時と編集時の評価規則を一致させられる。
|
||||
|
||||
```rust
|
||||
use decodal::{Engine, HostEnvironment};
|
||||
|
||||
struct AppEnvironment;
|
||||
|
||||
impl HostEnvironment for AppEnvironment {
|
||||
type Loader = ContentLoader;
|
||||
|
||||
fn create_loader(&self) -> Self::Loader {
|
||||
ContentLoader::new()
|
||||
}
|
||||
|
||||
fn configure_engine(
|
||||
&self,
|
||||
engine: &mut Engine<Self::Loader>,
|
||||
) -> decodal::Result<()> {
|
||||
engine.bind_global("Site", site_schema())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
transport 非依存の tooling は `decodal_language_service::LanguageService::new(&environment)` で構築する。
|
||||
LSP integration では `decodal_lsp::LspEnvironment` を実装し、environment factory から server を起動する。
|
||||
|
||||
```rust
|
||||
use decodal_lsp::{LspEnvironment, run_stdio};
|
||||
|
||||
impl LspEnvironment for AppEnvironment {}
|
||||
|
||||
run_stdio(|initialize| {
|
||||
let _ = initialize;
|
||||
Ok(AppEnvironment)
|
||||
})?;
|
||||
```
|
||||
|
||||
factory は server capability の応答前に client の `InitializeParams` を一度受け取る。
|
||||
`LspEnvironment` の document lifecycle hooks を使うと、未保存の DCDL、Markdown、その他の resource を host-owned overlay に反映できる。
|
||||
|
||||
## JavaScript and browser environments
|
||||
|
||||
`decodal-wasm` の `DecodalLanguageService` は filesystem や仮想 project を仮定しない。
|
||||
JavaScript 側が `globals`、`loadImport`、`completeImport` を提供する。
|
||||
|
||||
```js
|
||||
import init, { DecodalLanguageService } from 'decodal-wasm';
|
||||
|
||||
await init();
|
||||
|
||||
const files = {
|
||||
'schema.dcdl': 'Server = { port = Int; };',
|
||||
};
|
||||
|
||||
const service = new DecodalLanguageService({
|
||||
globals: {
|
||||
App: {
|
||||
enabled: { $decodal: 'Bool', default: true },
|
||||
},
|
||||
},
|
||||
loadImport(_currentKey, specifier) {
|
||||
const key = specifier.replace(/^\.\//, '');
|
||||
return { kind: 'source', key, name: key, source: files[key] };
|
||||
},
|
||||
completeImport() {
|
||||
return ['./schema.dcdl'];
|
||||
},
|
||||
});
|
||||
|
||||
const result = service.evaluate('main.dcdl', 'main.dcdl', 'App');
|
||||
```
|
||||
|
||||
JavaScript の primitive、array、plain object は concrete value になる。
|
||||
range は `$decodal` descriptor で指定する。
|
||||
|
||||
- `{ $decodal: 'String' | 'Int' | 'Float' | 'Bool' }`
|
||||
- `{ $decodal: 'Unknown' }`
|
||||
- `{ $decodal: 'Array', item }`
|
||||
- `{ $decodal: 'Map', value }`
|
||||
- `{ $decodal: 'Object', fields, rest }`
|
||||
- `{ $decodal: 'Range', constraints, default }`
|
||||
|
||||
import callbacks は同期 API である。
|
||||
network resource などの非同期入力は、評価の前に preload または cache しておく。
|
||||
Reference in New Issue
Block a user