Refocus manual on public language usage
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# モジュールと import
|
||||
|
||||
`import` は外部ファイルを読み込み、そのファイルの評価結果を返す。
|
||||
`import` はホストが解決した DCDL module または structured value を返す。
|
||||
|
||||
## 構文
|
||||
|
||||
@@ -8,79 +8,37 @@
|
||||
import "./config.dcdl"
|
||||
```
|
||||
|
||||
import specifier は文字列リテラルとする。
|
||||
パスリテラル構文は採用しない。
|
||||
import specifier は string literal である。
|
||||
specifier が path、URL、resource name のどれを表すかはホストが決める。
|
||||
|
||||
## モジュール
|
||||
|
||||
import 先はモジュール単位で読み込まれる。
|
||||
ただし、モジュール全体を即時評価する必要はない。
|
||||
各フィールドは thunk として保持され、必要になったときだけ評価される。
|
||||
|
||||
top-level に field 定義列を書いた module は、recursive module scope を作る。
|
||||
つまり、top-level field は同じ module の他の top-level field から識別子として参照できる。
|
||||
top-level に field 定義列を書いた module は recursive module scope を作る。
|
||||
top-level field は同じ module の他の top-level field から identifier として参照できる。
|
||||
|
||||
```dcdl
|
||||
schema = {
|
||||
hoge = String;
|
||||
name = String;
|
||||
};
|
||||
|
||||
result = schema;
|
||||
```
|
||||
|
||||
この場合、`result` の右辺の `schema` は同じ module の top-level field `schema` を参照する。
|
||||
通常の object literal 内の field は、その object 内の sibling field を暗黙には識別子として参照できない。
|
||||
object 内の値を参照する場合は、外側で束縛された値や明示的な path reference を使う。
|
||||
通常の object literal の field は sibling field を identifier として暗黙参照しない。
|
||||
object 内の値を参照する場合は、外側で束縛された値または明示的な path reference を使う。
|
||||
|
||||
## ImportLoader
|
||||
module とその field は遅延評価される。
|
||||
import した module の未参照 field は評価されない。
|
||||
|
||||
`import` specifier の解決は処理系 core ではなく host 側の `ImportLoader` が行う。
|
||||
CLI では、specifier を現在の module path からの相対 path として解決する。
|
||||
組み込み利用では、resource table や static source map など、filesystem 以外の loader を使える。
|
||||
## Host-defined resolution
|
||||
|
||||
module cache の key は loader が返す安定 key を使う。
|
||||
CLI では canonical path を key とする。
|
||||
Decodal は import specifier に対する filesystem や network の規則を定義しない。
|
||||
ホストが import 元の module と specifier を受け取り、次のどちらかを返す。
|
||||
|
||||
### 構造化 import
|
||||
- DCDL source
|
||||
- ホストが構築した structured value
|
||||
|
||||
`ImportLoader::load` は DCDL source または host が構築した `Value` を import 結果として返す。
|
||||
Markdown、JSON、TOML などの解釈規則は core に固定せず、loader がファイル種別を判定して構造化する。
|
||||
|
||||
```rust
|
||||
use decodal::{Value, ImportLoader, LoadedImport};
|
||||
|
||||
struct ContentLoader;
|
||||
|
||||
impl ImportLoader for ContentLoader {
|
||||
fn load(
|
||||
&mut self,
|
||||
current_key: Option<&str>,
|
||||
specifier: &str,
|
||||
) -> decodal::Result<LoadedImport> {
|
||||
if specifier.ends_with(".md") {
|
||||
let markdown = read_content(current_key, specifier)?;
|
||||
let parsed = parse_frontmatter(&markdown)?;
|
||||
return Ok(LoadedImport::value(
|
||||
parsed.key,
|
||||
Value::object([
|
||||
("frontmatter", parsed.frontmatter),
|
||||
("body", Value::string(parsed.body)),
|
||||
]),
|
||||
));
|
||||
}
|
||||
|
||||
let source = read_dcdl(current_key, specifier)?;
|
||||
Ok(LoadedImport::source(
|
||||
source.key,
|
||||
specifier,
|
||||
source.text,
|
||||
))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`read_content` と `parse_frontmatter` は host 独自の処理であり、Decodal core は Markdown や YAML parser に依存しない。
|
||||
上の loader を使うと、DCDL 側から次のように扱える。
|
||||
後者を使うと、Markdown、JSON、TOML などをホスト独自の規則で構造化し、通常の Decodal value として扱える。
|
||||
|
||||
```dcdl
|
||||
Post = {
|
||||
@@ -91,69 +49,40 @@ Post = {
|
||||
body = String;
|
||||
};
|
||||
|
||||
post = Post & import "./hello.md";
|
||||
title = post.frontmatter.title;
|
||||
body = post.body;
|
||||
post = (import "./hello.md") as Post;
|
||||
```
|
||||
|
||||
`LoadedImport::Value` は通常の concrete runtime value に internalize される。
|
||||
そのため、path reference、object composition、constraint validation、materialize は source 由来の値と同じ規則を使う。
|
||||
安定した `key` が同じ構造化 import は、engine 内で同じ値としてキャッシュされる。
|
||||
|
||||
`load` が唯一の import hook である。
|
||||
loader は拡張子、media type、または host 独自の規則で振り分け、対応する `LoadedImport` variant を直接返す。
|
||||
structured value も path reference、composition、constraint validation、materialization では DCDL source 由来の値と同じ規則に従う。
|
||||
loader API と diagnostic provenance は [Embedding](../embedding.md#imports) を参照する。
|
||||
|
||||
## 循環 import
|
||||
|
||||
モジュール間に循環参照があっても、必要なフィールドの依存関係が循環していなければ評価できる。
|
||||
|
||||
例:
|
||||
module 間に循環参照があっても、実際に評価される field の依存関係が循環していなければ成功する。
|
||||
|
||||
```dcdl
|
||||
# main.dcdl
|
||||
{
|
||||
schema = {
|
||||
hoge = String;
|
||||
};
|
||||
schema = {
|
||||
name = String;
|
||||
};
|
||||
|
||||
result = (import "./func.dcdl")(schema);
|
||||
}
|
||||
result = (import "./func.dcdl")(schema);
|
||||
```
|
||||
|
||||
```dcdl
|
||||
# func.dcdl
|
||||
(input: (import "./main.dcdl").schema) =>
|
||||
{
|
||||
# ...
|
||||
}
|
||||
(input: (import "./main.dcdl").schema) => input
|
||||
```
|
||||
|
||||
`func.dcdl` は `main.dcdl` を import しているが、参照しているのは `main.schema` である。
|
||||
`main.schema` が `main.result` に依存していなければ、この循環 import は成立する。
|
||||
この例で `func.dcdl` は `main.dcdl` を import するが、参照する `schema` は `result` に依存しないため評価できる。
|
||||
評価中の同じ field へ再び到達した場合は循環依存の diagnostic になる。
|
||||
|
||||
## import の評価単位
|
||||
## import の失敗
|
||||
|
||||
実装上は、以下の単位で管理するのが自然である。
|
||||
次の状態は import failure になる。
|
||||
|
||||
```text
|
||||
Module main
|
||||
schema -> thunk
|
||||
result -> thunk
|
||||
|
||||
Module func
|
||||
root -> thunk
|
||||
```
|
||||
|
||||
各 thunk は一度だけ評価して memoize する。
|
||||
評価中に同じ thunk へ戻った場合は循環依存としてエラーにする。
|
||||
|
||||
## import 失敗
|
||||
|
||||
以下は import 失敗として扱う。
|
||||
|
||||
- ファイルが存在しない。
|
||||
- ファイルが読めない。
|
||||
- import 先の構文解析に失敗する。
|
||||
- import 先の評価で必要な値がエラーになる。
|
||||
- host による非 DCDL content の読み込みまたは構造化に失敗する。
|
||||
- 実装が禁止する import 循環に該当する。
|
||||
- ホストが specifier を解決できない。
|
||||
- resource を読み込めない。
|
||||
- DCDL source の構文解析に失敗する。
|
||||
- 必要な import 先の値を評価できない。
|
||||
- structured value の読み込みまたは変換に失敗する。
|
||||
- 評価対象の依存関係が循環する。
|
||||
|
||||
Reference in New Issue
Block a user