Add bilingual manual and localized docs routes
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
# 制約と default
|
||||
|
||||
constraint は、値が満たすべき範囲を表す。
|
||||
|
||||
```dcdl
|
||||
Int
|
||||
String
|
||||
>= 1
|
||||
<= 65535
|
||||
/Hello! .*/
|
||||
```
|
||||
|
||||
constraint は `&` で合成できる。
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
NarrowedPort = Port & > 443;
|
||||
```
|
||||
|
||||
合成結果は両辺を同時に満たす範囲になる。
|
||||
両立しない constraint は conflict になる。
|
||||
|
||||
```dcdl
|
||||
Int & String # conflict
|
||||
> 10 & < 5 # conflict
|
||||
Int & > 10 & < 11 # conflict: integer candidate does not exist
|
||||
```
|
||||
|
||||
## Primitive and comparison constraints
|
||||
|
||||
組み込みの primitive range は次の通りである。
|
||||
|
||||
```dcdl
|
||||
String
|
||||
Int
|
||||
Float
|
||||
Bool
|
||||
```
|
||||
|
||||
異なる primitive type を `&` で合成すると conflict になる。
|
||||
数値比較 constraint は上下限として合成され、空の範囲になる場合は conflict になる。
|
||||
|
||||
```dcdl
|
||||
Int & >= 1 & <= 65535 & > 443
|
||||
```
|
||||
|
||||
`Int` の比較 constraint は integer literal を使う。
|
||||
`Float` の比較 constraint は integer または float literal を使える。
|
||||
|
||||
## Unknown
|
||||
|
||||
`Unknown` はすべての Decodal value を含む最上位 range である。
|
||||
検査を無効化して具体値の情報を消す `Any` ではなく、値の範囲がまだ絞られていないことを表す。
|
||||
|
||||
```dcdl
|
||||
Int & Unknown # Int
|
||||
42 as Unknown # 42
|
||||
Unknown as Int # conflict
|
||||
```
|
||||
|
||||
`Unknown` 自体は concrete value を持たないため materialize できない。
|
||||
concrete value で絞り込むか `default` を指定する必要がある。
|
||||
|
||||
```dcdl
|
||||
Unknown default {}
|
||||
```
|
||||
|
||||
## Regex constraints
|
||||
|
||||
regex literal は string constraint である。
|
||||
|
||||
```dcdl
|
||||
Host = /^api-[0-9]+$/;
|
||||
```
|
||||
|
||||
複数の regex constraint を合成した場合、concrete string はすべてに一致する必要がある。
|
||||
|
||||
```dcdl
|
||||
String & /^a/ & /z$/
|
||||
```
|
||||
|
||||
regex constraint 同士の交差は合成時には判定されない。
|
||||
そのため、次の範囲は合成時には conflict にならず、concrete value の検証時に失敗する。
|
||||
|
||||
```dcdl
|
||||
String & /^a$/ & /^b$/
|
||||
```
|
||||
|
||||
Rust runtime では regex engine を `regex` feature で有効にする。
|
||||
feature が無効な場合、regex constraint の concrete value 検証は unsupported feature diagnostic になる。
|
||||
|
||||
## Array constraints
|
||||
|
||||
array constraint は `[...T]` と書き、すべての要素へ `T` を適用する。
|
||||
要素 constraint は必須である。
|
||||
|
||||
```dcdl
|
||||
Names = [...String];
|
||||
PositiveInts = [...(Int & > 0)];
|
||||
```
|
||||
|
||||
concrete array と合成した場合、各要素は `T` に対して `as` と同じ規則で絞り込まれる。
|
||||
空 array は任意の array constraint を満たす。
|
||||
|
||||
要素 constraint が object range の場合、右辺にだけある field は abstract のまま各要素へ残る。
|
||||
左辺にしかない field は右辺の field domain 外なので conflict になる。
|
||||
|
||||
## Associative-array and object rest constraints
|
||||
|
||||
associative-array constraint は `{...T}` と書き、object の任意の field value へ `T` を適用する。
|
||||
|
||||
```dcdl
|
||||
Services = {...{
|
||||
port = Int;
|
||||
enabled = Bool default true;
|
||||
}};
|
||||
```
|
||||
|
||||
key は列挙されず、空 object も許容される。
|
||||
named field を持つ object の末尾へ `...T` を書くと、列挙されていない field だけに `T` を適用できる。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
enabled = Bool default true;
|
||||
...Unknown
|
||||
}
|
||||
```
|
||||
|
||||
rest constraint は field を生成しない。
|
||||
実在する追加 field の検証にだけ使われる。
|
||||
|
||||
## default
|
||||
|
||||
`default` は constraint ではない。
|
||||
materialize 時に concrete value がない場合だけ使われる fallback である。
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
|
||||
Service = {
|
||||
port = Port default 8080;
|
||||
};
|
||||
```
|
||||
|
||||
明示値が合成された場合は明示値が使われ、`default` は評価されない。
|
||||
|
||||
```dcdl
|
||||
Config = {
|
||||
port = 9000;
|
||||
} as Service;
|
||||
```
|
||||
|
||||
明示値がない場合、materialize 時に `8080` が採用され、`Port` を満たすか検証される。
|
||||
|
||||
## default composition
|
||||
|
||||
- `&` で片方だけが `default` を持つ場合、その `default` は保持される。
|
||||
- `&` で両辺が異なる `default` を持つ場合は conflict になる。
|
||||
- constraint と concrete value の `&` が成功した場合、結果は concrete value になり `default` は残らない。
|
||||
- `//` は右辺優先なので、同じ field では右辺の値または `default` が左辺を置き換える。
|
||||
- `as` は右辺の `default` を左辺へ注入しない。ただし右辺にだけ存在する field は、その field が持つ `default` とともに abstract なまま結果へ残る。
|
||||
|
||||
`default` expression は materialize 時に必要になった時点で評価され、同じ range の constraint を満たす必要がある。
|
||||
@@ -0,0 +1,48 @@
|
||||
# 遅延評価
|
||||
|
||||
Decodal は値を必要になった時点で評価する。
|
||||
|
||||
## 遅延評価される値
|
||||
|
||||
次の値は参照または materialize されるまで評価されない。
|
||||
|
||||
- module の root と top-level field
|
||||
- object field
|
||||
- `let` binding
|
||||
- function argument
|
||||
- `default` expression
|
||||
- import 先の値
|
||||
|
||||
同じ binding を複数回参照した場合、その評価結果は再利用される。
|
||||
このため、未参照の field や function argument にある失敗は、値が必要になるまで発生しない。
|
||||
|
||||
```dcdl
|
||||
let
|
||||
safe = 42;
|
||||
unused = missing_name;
|
||||
in
|
||||
safe
|
||||
```
|
||||
|
||||
この式は `unused` を参照しないため `42` になる。
|
||||
|
||||
## 循環検出
|
||||
|
||||
評価中の値が自身へ再び依存した場合は cycle diagnostic になる。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
a = b + 1;
|
||||
b = a + 1;
|
||||
}
|
||||
```
|
||||
|
||||
module や import の参照関係自体が循環していても、実際に評価される field の依存関係が循環していなければ評価できる。
|
||||
|
||||
## 評価と materialize
|
||||
|
||||
通常の評価結果には、constraint、`Unknown`、`default`、function などの abstract value が残り得る。
|
||||
外部へ concrete data として取り出すときに materialize を行う。
|
||||
|
||||
この分離により、schema を値として合成し、必要な field だけを評価し、`default` の選択を出力時まで遅らせられる。
|
||||
詳細は [materialize とエラー](./materialization-and-errors.md) を参照する。
|
||||
@@ -0,0 +1,166 @@
|
||||
# 例
|
||||
|
||||
この章では、Decodal の主要な記法を組み合わせた例を示す。
|
||||
|
||||
## 基本的な設定スキーマ
|
||||
|
||||
```dcdl
|
||||
Host = String;
|
||||
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
NarrowedPort = Port & > 443;
|
||||
|
||||
MyConfig = {
|
||||
host = Host;
|
||||
port = NarrowedPort default 8080;
|
||||
feature_hoge = {
|
||||
enable = Bool default true;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
};
|
||||
|
||||
NewConfig = MyConfig & {
|
||||
host = "127.0.0.1";
|
||||
port = 8000;
|
||||
};
|
||||
|
||||
disabled_config = NewConfig & {
|
||||
feature_hoge.enable = false;
|
||||
};
|
||||
|
||||
enabled_config = NewConfig;
|
||||
```
|
||||
|
||||
## 配列スキーマ
|
||||
|
||||
```dcdl
|
||||
Services = [...{
|
||||
name = String;
|
||||
port = Int default 8080;
|
||||
}];
|
||||
|
||||
[
|
||||
{ name = "api"; },
|
||||
{ name = "worker"; port = 9000; },
|
||||
] as Services
|
||||
```
|
||||
|
||||
抽象配列には要素制約が必須である。
|
||||
この例では、1 番目の要素の `port` は `8080` に materialize される。
|
||||
|
||||
## 連想配列と範囲絞り込み
|
||||
|
||||
```dcdl
|
||||
Service = {
|
||||
port = Int;
|
||||
enabled = Bool default true;
|
||||
};
|
||||
|
||||
services = {
|
||||
api = { port = 8080; };
|
||||
worker = { port = 8081; enabled = false; };
|
||||
} as {...Service};
|
||||
```
|
||||
|
||||
`api` と `worker` は任意の key であり、それぞれの value は `Service` より狭い範囲へ絞り込まれる。
|
||||
`as` の結果では `api.enabled` は abstract のまま残り、最終的に結果全体を materialize した時点で default の `true` が使われる。
|
||||
|
||||
## 関数と制約
|
||||
|
||||
```dcdl
|
||||
let
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
add_offset = (base: Port, offset: Int) => base + offset;
|
||||
in
|
||||
add_offset(8000, 80)
|
||||
```
|
||||
|
||||
評価結果:
|
||||
|
||||
```text
|
||||
8080
|
||||
```
|
||||
|
||||
## match
|
||||
|
||||
```dcdl
|
||||
(
|
||||
input_a: {
|
||||
hoge = Int & >= 0;
|
||||
},
|
||||
input_b: {
|
||||
fuga = 20;
|
||||
}
|
||||
) =>
|
||||
let
|
||||
inputs = {
|
||||
a = input_a;
|
||||
b = input_b;
|
||||
};
|
||||
in
|
||||
{
|
||||
foo = match inputs.a.hoge {
|
||||
>= 20: {
|
||||
value = 200;
|
||||
};
|
||||
>= 10: {
|
||||
value = 100;
|
||||
};
|
||||
_: {
|
||||
value = 300;
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
`match` は上から順に評価されるため、広い条件より狭い条件を先に書く。
|
||||
|
||||
## deep patch
|
||||
|
||||
```dcdl
|
||||
Base = {
|
||||
feature_hoge = {
|
||||
enable = Bool default true;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
};
|
||||
|
||||
Patched = Base // {
|
||||
feature_hoge.enable = false;
|
||||
};
|
||||
```
|
||||
|
||||
`Patched` は以下に相当する。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 循環 import
|
||||
|
||||
```dcdl
|
||||
# main.dcdl
|
||||
{
|
||||
schema = {
|
||||
hoge = String;
|
||||
};
|
||||
|
||||
result = (import "./func.dcdl")(schema);
|
||||
}
|
||||
```
|
||||
|
||||
```dcdl
|
||||
# func.dcdl
|
||||
(input: (import "./main.dcdl").schema) =>
|
||||
{
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
`func.dcdl` は `main.dcdl` を import しているが、参照しているのは `main.schema` である。
|
||||
`main.schema` が `main.result` に依存していなければ、この循環 import は成立する。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 算術式
|
||||
|
||||
Decodal は具体的な数値に対する算術演算をサポートする。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
workers = 2 + 2;
|
||||
timeout = 30.0 / 2;
|
||||
port = 8000 + 80;
|
||||
negative = -1;
|
||||
}
|
||||
```
|
||||
|
||||
## 演算子
|
||||
|
||||
- `+`: 加算
|
||||
- `-`: 減算
|
||||
- `*`: 乗算
|
||||
- `/`: 除算
|
||||
- 単項 `-`: 符号反転
|
||||
|
||||
`*` と `/` は `+` と `-` より高い優先順位を持つ。
|
||||
括弧を使ってグループ化を明示できる。
|
||||
|
||||
```dcdl
|
||||
2 + 3 * 4 # 14
|
||||
(2 + 3) * 4 # 20
|
||||
```
|
||||
|
||||
## 数値の扱い
|
||||
|
||||
算術演算には具体的な `Int` または `Float` の operand が必要である。
|
||||
`Int + Int`、`Int - Int`、`Int * Int` は、overflow が発生しない場合に `Int` を返す。
|
||||
`Int` と `Float` が混在する演算は `Float` を返す。
|
||||
除算は常に `Float` を返す。
|
||||
|
||||
ゼロ除算と整数 overflow は評価エラーになる。
|
||||
|
||||
算術式は、default や数値制約を含め、具体的な数値式が必要な任意の場所で使える。
|
||||
|
||||
```dcdl
|
||||
port = Int & > 4000 + 42 default 8080;
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Array Expression
|
||||
|
||||
array expression は、順序付きの値の列を表す。
|
||||
|
||||
```dcdl
|
||||
[1, 2, 3]
|
||||
["a", "b", "c"]
|
||||
```
|
||||
|
||||
配列リテラルは concrete value であり、要素型を揃える必要はない。
|
||||
|
||||
```dcdl
|
||||
["api", 8080, true]
|
||||
```
|
||||
|
||||
## Array constraint
|
||||
|
||||
配列制約は `[` の直後に ellipsis を置き、その後へ要素制約を記述する。
|
||||
|
||||
```dcdl
|
||||
Names = [...String];
|
||||
Ports = [...(Int & >= 1 & <= 65535)];
|
||||
```
|
||||
|
||||
`[...T]` は、すべての要素が `T` を満たす長さ 0 以上の配列を表す。
|
||||
空配列は任意の配列制約を満たす。
|
||||
|
||||
```dcdl
|
||||
[...String] & []
|
||||
[...String] & ["api", "worker"]
|
||||
```
|
||||
|
||||
要素制約は object range にもできる。
|
||||
右辺だけにある default 付き field は、配列へ制約を合成した時点では abstract のまま各要素に残る。
|
||||
その配列を後から materialize した場合にだけ default が使われる。
|
||||
|
||||
```dcdl
|
||||
Services = [...{
|
||||
name = String;
|
||||
enabled = Bool default true;
|
||||
}];
|
||||
|
||||
Services & [{ name = "api"; }]
|
||||
```
|
||||
|
||||
配列制約を concrete array に適用すると、各要素は要素 range に対して `as` と同じ規則で絞り込まれる。
|
||||
object の要素 range では左辺にしかない field がエラーになり、右辺にしかない field は abstract のまま残る。
|
||||
|
||||
要素制約のない抽象配列型は提供しない。
|
||||
旧来の `Array` primitive type は使用できず、`[...T]` の `T` は必須である。
|
||||
`[String]` は配列制約ではなく、未解決の `String` 制約を 1 要素に持つ concrete array になる。
|
||||
配列制約は要素範囲だけを表す。長さ制約、位置別の tuple 制約、unique 制約は持たない。
|
||||
|
||||
## Array concat
|
||||
|
||||
`++` は concrete array 同士を連結する。
|
||||
|
||||
```dcdl
|
||||
base = ["read", "write"];
|
||||
extra = ["admin"];
|
||||
roles = base ++ extra;
|
||||
```
|
||||
|
||||
`roles` は以下と同じ値になる。
|
||||
|
||||
```dcdl
|
||||
["read", "write", "admin"]
|
||||
```
|
||||
|
||||
`++` は配列要素を変換しない。
|
||||
左辺の要素の後に右辺の要素が並ぶ。
|
||||
@@ -0,0 +1,75 @@
|
||||
# Range Refinement
|
||||
|
||||
`narrower as wider` は、左辺が右辺より具体的で狭い範囲であることを確認し、両者を非対称に合成する演算である。
|
||||
|
||||
```dcdl
|
||||
Int & > 10 as Int & > 0
|
||||
```
|
||||
|
||||
この例は成功し、結果は左辺の狭い範囲 `Int & > 10` のままになる。逆向きの `Int as Int & > 0` は、左辺が右辺を満たすとは限らないため失敗する。
|
||||
|
||||
## Object
|
||||
|
||||
object では、右辺を field domain として左辺の field を確認する。
|
||||
|
||||
- 左辺にしかない field は、右辺の domain 外なのでエラーになる。
|
||||
- 両側にある field は、左辺が右辺より狭いかを再帰的に確認し、左辺の狭い結果を使う。
|
||||
- 右辺にしかない field は、default の有無に関係なく未評価のまま結果へ残す。
|
||||
- 結果の field order は右辺の順序に従う。
|
||||
|
||||
```dcdl
|
||||
partial = {
|
||||
port = 8080;
|
||||
} as {
|
||||
host = String;
|
||||
port = Int;
|
||||
enabled = Bool default true;
|
||||
};
|
||||
```
|
||||
|
||||
`partial.port` は `8080` に具体化される。`partial.host` と `partial.enabled` は右辺由来の abstract field のままであり、`as` は `enabled` の default を選択も force もしない。
|
||||
|
||||
その後 `partial` 全体を materialize すれば、通常の materialize 規則が適用される。この例では unresolved な `host` がエラーになり、`host` も具体化されていれば `enabled` の default がその時点で利用される。
|
||||
|
||||
## Default
|
||||
|
||||
default は範囲包含の根拠ではない。両側に同じ field がある場合、結果には左辺を使うため、右辺の default を左辺へ注入しない。
|
||||
|
||||
```dcdl
|
||||
Int & > 0 as (Int default 1)
|
||||
```
|
||||
|
||||
結果は default のない `Int & > 0` である。一方、右辺だけに残る object field は field 全体を保持するため、その abstract value が元から持つ default も保持される。
|
||||
|
||||
## Map and array ranges
|
||||
|
||||
`{...T}` は任意の object key を許可し、各 value が `T` より狭いことを確認する。
|
||||
|
||||
```dcdl
|
||||
ports = {
|
||||
http = 80;
|
||||
https = 443;
|
||||
} as {...(Int & >= 1 & <= 65535)};
|
||||
```
|
||||
|
||||
`[...T]` を右辺に使う場合も、すべての array element を `T` に対して絞り込む。
|
||||
|
||||
## Concrete right-hand ranges
|
||||
|
||||
primitive constraint や合成 constraint は通常どおり値を検証する。
|
||||
右辺が concrete scalar または array literal の場合は、左辺も同じ値または同じ長さ・要素構造である必要がある。
|
||||
function は右辺の範囲として使用できない。
|
||||
|
||||
左辺は concrete value に限らない。包含関係を判定できる constraint 同士であれば abstract value も使用できる。primitive type、numeric bound、同一 regex / predicate、array/map の要素範囲は包含確認の対象になる。
|
||||
|
||||
関数 parameter の `name: range` も、引数が force された時点で `as` と同じ絞り込み規則を使う。
|
||||
|
||||
## Precedence
|
||||
|
||||
`as` は `default` より低い、最も低い優先順位を持ち、左結合である。
|
||||
|
||||
```dcdl
|
||||
narrow & overrides as Wider
|
||||
```
|
||||
|
||||
これは `(narrow & overrides) as Wider` と解釈される。
|
||||
@@ -0,0 +1,27 @@
|
||||
# Composition Expression
|
||||
|
||||
composition expression は、複数の値・制約・構造を合成する式である。
|
||||
|
||||
## `&`
|
||||
|
||||
`&` は制約を保った合成を行う。
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
Config = MyConfig & { port = 8000; };
|
||||
```
|
||||
|
||||
object 同士では片側だけにある field も保持するため、`&` は方向を持たない。
|
||||
左辺が右辺より狭いことを確認し、右辺を field domain として合成したい場合は [`as`](./ascription.md) を使う。
|
||||
|
||||
## `//`
|
||||
|
||||
`//` は右辺優先の構造的 patch を行う。
|
||||
|
||||
```dcdl
|
||||
Patched = Base // {
|
||||
feature_hoge.enable = false;
|
||||
};
|
||||
```
|
||||
|
||||
詳細は [合成演算子](../operators.md) を参照する。
|
||||
@@ -0,0 +1,15 @@
|
||||
# Default Expression
|
||||
|
||||
`default` expression は、明示値が存在しない場合に materialize 時に採用される fallback を指定する。
|
||||
|
||||
```dcdl
|
||||
port = Int default 8080;
|
||||
```
|
||||
|
||||
`default` は制約ではない。
|
||||
詳細は [制約と default](../constraints-and-defaults.md) を参照する。
|
||||
|
||||
## 評価
|
||||
|
||||
fallback expression は materialize 時に必要になった場合だけ評価される。
|
||||
明示値がある場合、`default` は評価も採用もされない。
|
||||
@@ -0,0 +1,14 @@
|
||||
# Function Call Expression
|
||||
|
||||
function call expression は、関数値を引数に適用する式である。
|
||||
|
||||
```dcdl
|
||||
increment(41)
|
||||
```
|
||||
|
||||
## 評価
|
||||
|
||||
引数は関数本体から参照された時点で評価される。
|
||||
同じ引数を複数回参照した場合は評価結果が再利用される。
|
||||
parameter に range が指定されている場合、引数は `narrower as wider` と同じ規則で絞り込まれる。
|
||||
parameter 側だけにある field は abstract のまま残り、default はこの時点では選択されない。
|
||||
@@ -0,0 +1,14 @@
|
||||
# Function Expression
|
||||
|
||||
function expression は、引数を受け取り式を返す値である。
|
||||
|
||||
```dcdl
|
||||
(value: Int) => value + 1
|
||||
```
|
||||
|
||||
関数仕様の詳細は [関数](../functions.md) を参照する。
|
||||
|
||||
## 評価
|
||||
|
||||
関数は定義された lexical scope の bindings を参照する。
|
||||
関数本体は、関数値の生成時ではなく呼び出し時に評価される。
|
||||
@@ -0,0 +1,14 @@
|
||||
# Identifier Expression
|
||||
|
||||
identifier expression は、lexical scope に束縛された名前を参照する式である。
|
||||
|
||||
```dcdl
|
||||
Port
|
||||
MyConfig
|
||||
mkConfig
|
||||
```
|
||||
|
||||
## 評価
|
||||
|
||||
識別子は対応する binding の値を必要になった時点で評価する。
|
||||
束縛が存在しない場合は未定義識別子エラーになる。
|
||||
@@ -0,0 +1,14 @@
|
||||
# Import Expression
|
||||
|
||||
import expression は、ホストが解決した DCDL module または structured value を返す。
|
||||
|
||||
```dcdl
|
||||
import "./config.dcdl"
|
||||
```
|
||||
|
||||
specifier は string literal であり、path や resource name としての解釈はホストが定義する。
|
||||
詳しくは [モジュールと import](../modules-and-imports.md) を参照する。
|
||||
|
||||
## 評価
|
||||
|
||||
import 先は遅延評価され、参照されない field は評価されない。
|
||||
@@ -0,0 +1,26 @@
|
||||
# Expression
|
||||
|
||||
この章では、言語が評価対象として持つ式の分類を定義する。
|
||||
|
||||
式は、値・制約・構造・計算を表す基本単位である。
|
||||
|
||||
```text
|
||||
Expr
|
||||
├─ literal
|
||||
├─ identifier
|
||||
├─ path reference
|
||||
├─ object
|
||||
├─ map constraint
|
||||
├─ array
|
||||
├─ array constraint
|
||||
├─ function
|
||||
├─ function call
|
||||
├─ let
|
||||
├─ match
|
||||
├─ import
|
||||
├─ composition
|
||||
├─ range refinement (`as`)
|
||||
└─ default
|
||||
```
|
||||
|
||||
各式の個別仕様へのリンクは [Manual Index](../../index.md) に集約する。
|
||||
@@ -0,0 +1,16 @@
|
||||
# Let Expression
|
||||
|
||||
let expression は、ローカル束縛を作る。
|
||||
|
||||
```dcdl
|
||||
let
|
||||
base = 8000;
|
||||
offset = 80;
|
||||
in
|
||||
base + offset
|
||||
```
|
||||
|
||||
## 評価
|
||||
|
||||
binding は参照された時点で評価される。
|
||||
参照されない binding は評価されず、同じ binding を複数回参照した場合は評価結果が再利用される。
|
||||
@@ -0,0 +1,20 @@
|
||||
# Literal Expression
|
||||
|
||||
literal expression は、ソース上に直接書かれる具体値である。
|
||||
|
||||
## 種類
|
||||
|
||||
```dcdl
|
||||
"hello"
|
||||
123
|
||||
3.14
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
## 対応する primitive type
|
||||
|
||||
- 文字列リテラルは `String` を満たす。
|
||||
- 整数リテラルは `Int` を満たす。
|
||||
- 浮動小数リテラルは `Float` を満たす。
|
||||
- `true` / `false` は `Bool` を満たす。
|
||||
@@ -0,0 +1,41 @@
|
||||
# 論理式と比較式
|
||||
|
||||
Decodal は具体的な `Bool` に対する論理演算と、具体的な scalar value に対する比較をサポートする。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
is_prod = env == "prod";
|
||||
high_port = port > 9000;
|
||||
enabled = is_prod && high_port;
|
||||
disabled = !enabled;
|
||||
}
|
||||
```
|
||||
|
||||
## 論理演算子
|
||||
|
||||
- `!expr` は具体的な `Bool` を反転する。
|
||||
- `lhs && rhs` は論理積を返す。
|
||||
- `lhs || rhs` は論理和を返す。
|
||||
|
||||
`&&` と `||` は短絡評価され、右辺は必要な場合にだけ評価される。
|
||||
論理演算の operand は具体的な `Bool` へ評価される必要がある。
|
||||
|
||||
## 比較演算子
|
||||
|
||||
- `==`
|
||||
- `!=`
|
||||
- `<`
|
||||
- `<=`
|
||||
- `>`
|
||||
- `>=`
|
||||
|
||||
`==` と `!=` は、`String`、`Bool`、`Int`、`Float` の具体的な scalar value を比較する。
|
||||
`Int` と `Float` は数値として相互に比較できる。
|
||||
|
||||
順序比較演算子 `<`、`<=`、`>`、`>=` は、具体的な数値だけを比較する。
|
||||
これらは `> 443` のような prefix comparison constraint とは別のものである。
|
||||
|
||||
```dcdl
|
||||
port = Int & > 443 default 9443;
|
||||
is_high = port > 9000;
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
# Match Expression
|
||||
|
||||
match expression は、対象値を上から順に pattern と照合し、最初に一致した分岐を採用する。
|
||||
|
||||
```dcdl
|
||||
foo = match inputs.a.hoge {
|
||||
>= 20: {
|
||||
value = 200;
|
||||
};
|
||||
>= 10: {
|
||||
value = 100;
|
||||
};
|
||||
_: {
|
||||
value = 300;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`_` は fallback pattern である。
|
||||
|
||||
## 順序
|
||||
|
||||
分岐は順序付きであり、「最も具体的な pattern」を自動選択しない。
|
||||
広い条件を先に書くと、後続の狭い条件には到達しない。
|
||||
@@ -0,0 +1,88 @@
|
||||
# Object Expression
|
||||
|
||||
object expression は、名前付き field の集合を表す。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
host = "127.0.0.1";
|
||||
port = 8000;
|
||||
}
|
||||
```
|
||||
|
||||
object は設定値にもスキーマにも使う。
|
||||
|
||||
```dcdl
|
||||
MyConfig = {
|
||||
host = String;
|
||||
port = Int default 8080;
|
||||
};
|
||||
```
|
||||
|
||||
## Dot-path Field
|
||||
|
||||
ネストした field はドットパスでも定義できる。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge.enable = false;
|
||||
}
|
||||
```
|
||||
|
||||
これは以下と同じ構造を表す。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Map constraint
|
||||
|
||||
連想配列の制約は、object の任意の field value に同じ schema を適用する。
|
||||
|
||||
```dcdl
|
||||
Services = {...{
|
||||
port = Int;
|
||||
enabled = Bool default true;
|
||||
}};
|
||||
```
|
||||
|
||||
`{...T}` は key の集合を固定せず、すべての value が `T` を満たす object を表す。
|
||||
空 object も許容される。materialize 後は named fields を持つ object と同じ object data になる。
|
||||
|
||||
```dcdl
|
||||
services = {
|
||||
api = { port = 8080; };
|
||||
worker = { port = 8081; enabled = false; };
|
||||
} as Services;
|
||||
```
|
||||
|
||||
各 entry は `as` によって右辺の field domain 内へ絞り込まれるため、左辺に `port` や `enabled` 以外の field があればエラーになる。
|
||||
右辺にしかない field は default の有無にかかわらず abstract のまま残る。
|
||||
host から渡した object は識別子構文に収まらない文字列 key も保持できるが、DCDL source の object field name は通常の識別子に限られる。
|
||||
|
||||
## Object rest constraint
|
||||
|
||||
固定 field と任意 key の value range は、一つの object に混在できる。
|
||||
|
||||
```dcdl
|
||||
OpenConfig = {
|
||||
enabled = Bool default true;
|
||||
...Unknown
|
||||
};
|
||||
```
|
||||
|
||||
`...T` は明示されていない残余 field にだけ適用する。明示 field にはそれぞれの field range を適用し、rest range を重ねて適用しない。
|
||||
rest constraint は field を生成せず、materialize 時には実際に存在する追加 field だけを出力する。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
enabled = false;
|
||||
plugin = { name = "cache"; };
|
||||
} as OpenConfig
|
||||
```
|
||||
|
||||
`...T` は object の末尾に一つだけ書ける。省略した object は閉じており、`as` の左辺に未宣言 field があればエラーになる。
|
||||
named field を持たない `{...T}` は abstract な map constraint であり、単独で materialize するには `default` または concrete value が必要になる。
|
||||
@@ -0,0 +1,16 @@
|
||||
# Path Reference Expression
|
||||
|
||||
path reference expression は、object のフィールドを参照する式である。
|
||||
|
||||
```dcdl
|
||||
config.host
|
||||
config.feature_hoge.enable
|
||||
```
|
||||
|
||||
## 評価
|
||||
|
||||
左側の式を object として評価し、指定された field を参照する。
|
||||
参照先 field は必要になるまで評価されない。
|
||||
|
||||
存在しない field への参照は diagnostic になる。
|
||||
明示的な `Unknown` range は存在しないfieldを表さないため、fieldの有無を曖昧にはしない。
|
||||
@@ -0,0 +1,41 @@
|
||||
# 関数
|
||||
|
||||
関数は値を受け取り、値を返す純粋な式である。
|
||||
|
||||
## 構文
|
||||
|
||||
```dcdl
|
||||
(value: Int) => value + 1
|
||||
```
|
||||
|
||||
関数呼び出しは通常の呼び出し構文で行う。
|
||||
|
||||
```dcdl
|
||||
let
|
||||
increment = (value: Int) => value + 1;
|
||||
in
|
||||
increment(41)
|
||||
```
|
||||
|
||||
複数の parameter を指定できる。
|
||||
|
||||
```dcdl
|
||||
(input_a: { hoge = Int & >= 0; }, input_b: { fuga = Int; }) =>
|
||||
{
|
||||
hoge = input_a.hoge;
|
||||
fuga = input_b.fuga;
|
||||
}
|
||||
```
|
||||
|
||||
parameter range は省略できる。
|
||||
range がある場合、引数は参照された時点で `as` と同じ規則によって検証・絞り込みされる。
|
||||
|
||||
## Scope and evaluation
|
||||
|
||||
関数は lexical scope を持ち、定義された場所の bindings を参照する。
|
||||
引数は遅延評価され、関数本体から参照されない引数は評価されない。
|
||||
再帰的な field または argument の依存は cycle diagnostic になる。
|
||||
|
||||
関数は中間値として参照・呼び出しできるが、data として materialize できない。
|
||||
未適用の関数が materialize 対象に残っている場合は diagnostic になる。
|
||||
関数値同士の等価性は定義されず、`&` で関数値同士を合成すると conflict になる。
|
||||
@@ -0,0 +1,113 @@
|
||||
# 文法
|
||||
|
||||
このページでは、Decodal ソーステキストの文法を定義する。
|
||||
|
||||
## 字句文法
|
||||
|
||||
```ebnf
|
||||
source_character = ? any Unicode scalar value ? ;
|
||||
newline = "\n" | "\r\n" | "\r" ;
|
||||
space = " " | "\t" | newline ;
|
||||
comment = "#" , { ? any character except newline ? } ;
|
||||
|
||||
digit = "0" … "9" ;
|
||||
letter = "A" … "Z" | "a" … "z" ;
|
||||
identifier = letter , { letter | digit | "_" } ;
|
||||
|
||||
integer = digit , { digit } ;
|
||||
float = digit , { digit } , "." , digit , { digit } ;
|
||||
|
||||
string = '"' , { string_character | escape } , '"' ;
|
||||
string_character = ? any character except '"', "\\", or newline ? ;
|
||||
escape = "\\" , source_character ;
|
||||
|
||||
regex = "/" , regex_character , { regex_character } , "/" ;
|
||||
regex_character = escape | ? any character except "/", "\\", or newline ? ;
|
||||
```
|
||||
|
||||
空白とコメントは token を区切り、それ以外では parser に無視される。
|
||||
|
||||
## 構文文法
|
||||
|
||||
```ebnf
|
||||
module = { statement } ;
|
||||
statement = field_definition , [ ";" ]
|
||||
| expression , [ ";" ] ;
|
||||
|
||||
expression = as_expression ;
|
||||
|
||||
as_expression = default_expression , { "as" , default_expression } ;
|
||||
default_expression = patch_expression , [ "default" , default_expression ] ;
|
||||
patch_expression = compose_expression , { "//" , compose_expression } ;
|
||||
compose_expression = logical_or_expression , { "&" , logical_or_expression } ;
|
||||
|
||||
logical_or_expression = logical_and_expression , { "||" , logical_and_expression } ;
|
||||
logical_and_expression = comparison_expression , { "&&" , comparison_expression } ;
|
||||
comparison_expression = concat_expression , [ comparison_operator , concat_expression ] ;
|
||||
concat_expression = additive_expression , { "++" , additive_expression } ;
|
||||
additive_expression = multiplicative_expression , { ( "+" | "-" ) , multiplicative_expression } ;
|
||||
multiplicative_expression = unary_expression , { ( "*" | "/" ) , unary_expression } ;
|
||||
|
||||
unary_expression = [ "!" | "-" ] , postfix_expression ;
|
||||
postfix_expression = primary_expression , { call_suffix | path_suffix } ;
|
||||
call_suffix = "(" , [ argument_list ] , ")" ;
|
||||
path_suffix = "." , identifier ;
|
||||
|
||||
primary_expression = literal
|
||||
| identifier
|
||||
| comparison_constraint
|
||||
| map_constraint
|
||||
| object
|
||||
| array_constraint
|
||||
| array
|
||||
| let_expression
|
||||
| function_expression
|
||||
| match_expression
|
||||
| import_expression
|
||||
| "(" , expression , ")" ;
|
||||
|
||||
literal = string | integer | float | "true" | "false" | regex ;
|
||||
comparison_operator = "==" | "!=" | "<" | "<=" | ">" | ">=" ;
|
||||
comparison_constraint = ( "<" | "<=" | ">" | ">=" ) , expression ;
|
||||
|
||||
object = "{" , [ field_definition , { ";" , field_definition }
|
||||
, [ ";" , object_rest ] , [ ";" ] ] , "}" ;
|
||||
object_rest = "..." , expression ;
|
||||
map_constraint = "{" , "..." , expression , "}" ;
|
||||
field_definition = field_path , "=" , expression ;
|
||||
field_path = identifier , { "." , identifier } ;
|
||||
|
||||
array = "[" , [ expression , { "," , expression } , [ "," ] ] , "]" ;
|
||||
array_constraint = "[" , "..." , expression , [ "," ] , "]" ;
|
||||
|
||||
let_expression = "let" , { field_definition , ";" } , "in" , expression ;
|
||||
function_expression = "(" , [ parameter_list ] , ")" , "=>" , expression ;
|
||||
parameter_list = parameter , { "," , parameter } , [ "," ] ;
|
||||
parameter = identifier , [ ":" , expression ] ;
|
||||
|
||||
match_expression = "match" , expression , "{" , [ match_arm , { ";" , match_arm } , [ ";" ] ] , "}" ;
|
||||
match_arm = pattern , ":" , expression ;
|
||||
pattern = "_" | expression ;
|
||||
|
||||
import_expression = "import" , string ;
|
||||
argument_list = expression , { "," , expression } , [ "," ] ;
|
||||
```
|
||||
|
||||
## 優先順位
|
||||
|
||||
優先順位は高い順に以下の通りである。
|
||||
|
||||
1. 関数呼び出しとフィールドのパス参照
|
||||
2. 単項 `!` と `-`
|
||||
3. `*` と `/`
|
||||
4. `+` と `-`
|
||||
5. `++`
|
||||
6. `==`、`!=`、`<`、`<=`、`>`、`>=`
|
||||
7. `&&`
|
||||
8. `||`
|
||||
9. `&`
|
||||
10. `//`
|
||||
11. `default`
|
||||
12. `as`
|
||||
|
||||
二項演算子は左結合だが、`default` だけは右結合である。
|
||||
@@ -0,0 +1,12 @@
|
||||
# 言語仕様
|
||||
|
||||
この章では、Decodal source の構文と評価結果を決める規則を説明する。
|
||||
|
||||
- syntax と grammar
|
||||
- primitive、object、array、function
|
||||
- constraint、`Unknown`、`default`
|
||||
- `&`、`//`、`as` とその他の operators
|
||||
- module、import、lazy evaluation
|
||||
- materialization と diagnostics
|
||||
|
||||
章の一覧は [Manual Index](../index.md) を参照する。
|
||||
@@ -0,0 +1,73 @@
|
||||
# materialize とエラー
|
||||
|
||||
通常の評価結果には constraint、`default`、function などが残り得る。
|
||||
materialize は評価結果を外部へ渡せる concrete data に変換する。
|
||||
|
||||
## materialize の規則
|
||||
|
||||
materialize は次の処理を行う。
|
||||
|
||||
- 必要な field を評価する。
|
||||
- 明示値のない abstract range に `default` を適用する。
|
||||
- concrete value と採用した `default` が constraint を満たすか検証する。
|
||||
- `default` のない `Unknown` や他の未解決 range を拒否する。
|
||||
- 未適用の function など、data に変換できない値を拒否する。
|
||||
|
||||
```dcdl
|
||||
Service = {
|
||||
host = String;
|
||||
port = Int default 8080;
|
||||
};
|
||||
```
|
||||
|
||||
`Service` をそのまま materialize すると、`host` に concrete value も `default` もないため失敗する。
|
||||
|
||||
```dcdl
|
||||
Config = {
|
||||
host = "localhost";
|
||||
} as Service;
|
||||
```
|
||||
|
||||
`Config` を materialize すると次の data になる。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
host = "localhost";
|
||||
port = 8080;
|
||||
}
|
||||
```
|
||||
|
||||
明示値がある field では `default` は採用されない。
|
||||
|
||||
## Diagnostics
|
||||
|
||||
エラーは通常の値ではなく diagnostic として返される。
|
||||
式は diagnostic の種類や内容に基づいて分岐できない。
|
||||
|
||||
代表的な diagnostic は次の通りである。
|
||||
|
||||
- syntax error
|
||||
- unresolved identifier または field
|
||||
- type mismatch と constraint violation
|
||||
- `&` または `default` の conflict
|
||||
- cycle dependency
|
||||
- import failure
|
||||
- match failure
|
||||
- materialization failure
|
||||
|
||||
diagnostic は問題のある DCDL source span を示す。
|
||||
複数の式が conflict した場合は、関係する field、constraint、value、`default` の位置も示される。
|
||||
structured import の値に source span がない場合は、host が返した stable key と logical value path が示される。
|
||||
|
||||
## Fallback
|
||||
|
||||
`match` に fallback arm がなく、どの arm にも一致しない場合は diagnostic になる。
|
||||
|
||||
```dcdl
|
||||
match value {
|
||||
>= 10: "large";
|
||||
}
|
||||
```
|
||||
|
||||
Decodal は diagnostic を捕捉する汎用 `try / catch`、optional import、optional field access を提供しない。
|
||||
値がない場合の fallback は `default`、有限の値分岐は `match` で表現する。
|
||||
@@ -0,0 +1,88 @@
|
||||
# モジュールと import
|
||||
|
||||
`import` はホストが解決した DCDL module または structured value を返す。
|
||||
|
||||
## 構文
|
||||
|
||||
```dcdl
|
||||
import "./config.dcdl"
|
||||
```
|
||||
|
||||
import specifier は string literal である。
|
||||
specifier が path、URL、resource name のどれを表すかはホストが決める。
|
||||
|
||||
## モジュール
|
||||
|
||||
top-level に field 定義列を書いた module は recursive module scope を作る。
|
||||
top-level field は同じ module の他の top-level field から identifier として参照できる。
|
||||
|
||||
```dcdl
|
||||
schema = {
|
||||
name = String;
|
||||
};
|
||||
|
||||
result = schema;
|
||||
```
|
||||
|
||||
通常の object literal の field は sibling field を identifier として暗黙参照しない。
|
||||
object 内の値を参照する場合は、外側で束縛された値または明示的な path reference を使う。
|
||||
|
||||
module とその field は遅延評価される。
|
||||
import した module の未参照 field は評価されない。
|
||||
|
||||
## Host-defined resolution
|
||||
|
||||
Decodal は import specifier に対する filesystem や network の規則を定義しない。
|
||||
ホストが import 元の module と specifier を受け取り、次のどちらかを返す。
|
||||
|
||||
- DCDL source
|
||||
- ホストが構築した structured value
|
||||
|
||||
後者を使うと、Markdown、JSON、TOML などをホスト独自の規則で構造化し、通常の Decodal value として扱える。
|
||||
|
||||
```dcdl
|
||||
Post = {
|
||||
frontmatter = {
|
||||
title = String;
|
||||
draft = Bool default false;
|
||||
};
|
||||
body = String;
|
||||
};
|
||||
|
||||
post = (import "./hello.md") as Post;
|
||||
```
|
||||
|
||||
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 = {
|
||||
name = String;
|
||||
};
|
||||
|
||||
result = (import "./func.dcdl")(schema);
|
||||
```
|
||||
|
||||
```dcdl
|
||||
# func.dcdl
|
||||
(input: (import "./main.dcdl").schema) => input
|
||||
```
|
||||
|
||||
この例で `func.dcdl` は `main.dcdl` を import するが、参照する `schema` は `result` に依存しないため評価できる。
|
||||
評価中の同じ field へ再び到達した場合は循環依存の diagnostic になる。
|
||||
|
||||
## import の失敗
|
||||
|
||||
次の状態は import failure になる。
|
||||
|
||||
- ホストが specifier を解決できない。
|
||||
- resource を読み込めない。
|
||||
- DCDL source の構文解析に失敗する。
|
||||
- 必要な import 先の値を評価できない。
|
||||
- structured value の読み込みまたは変換に失敗する。
|
||||
- 評価対象の依存関係が循環する。
|
||||
@@ -0,0 +1,17 @@
|
||||
# 命名規約
|
||||
|
||||
identifier の大文字・小文字に言語上の意味はない。
|
||||
値、constraint、schema、派生設定はいずれも同じ式として扱われる。
|
||||
|
||||
読みやすさのため、次の命名を推奨する。
|
||||
|
||||
- object value: `lower_snake`
|
||||
- function: `lowerCamel`
|
||||
- primitive、schema、抽象的な constraint: `UpperCamel`
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1;
|
||||
Service = { port = Port; };
|
||||
service = { port = 8080; } as Service;
|
||||
mkService = (port: Port) => { port = port; };
|
||||
```
|
||||
@@ -0,0 +1,251 @@
|
||||
# 演算子
|
||||
|
||||
この章では、Decodal の演算子の意味を定義する。
|
||||
|
||||
## 演算子一覧
|
||||
|
||||
| 演算子 | 形 | 種類 | 対象 | 結果 / 意味 |
|
||||
|---|---|---|---|---|
|
||||
| `.` | `object.field` | field reference | object / abstract object | field value |
|
||||
| call | `fn(arg)` | function call | function | function result |
|
||||
| `!` | `!expr` | unary logical | concrete `Bool` | concrete `Bool` |
|
||||
| `-` | `-expr` | unary arithmetic | concrete `Int` / `Float` | negated number |
|
||||
| `*` | `lhs * rhs` | arithmetic | concrete `Int` / `Float` | numeric product |
|
||||
| `/` | `lhs / rhs` | arithmetic | concrete `Int` / `Float` | `Float` quotient |
|
||||
| `+` | `lhs + rhs` | arithmetic | concrete `Int` / `Float` | numeric sum |
|
||||
| `-` | `lhs - rhs` | arithmetic | concrete `Int` / `Float` | numeric difference |
|
||||
| `++` | `lhs ++ rhs` | array concat | concrete arrays | concatenated array |
|
||||
| `==` | `lhs == rhs` | equality | concrete scalar | concrete `Bool` |
|
||||
| `!=` | `lhs != rhs` | equality | concrete scalar | concrete `Bool` |
|
||||
| `<` | `lhs < rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` |
|
||||
| `<=` | `lhs <= rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` |
|
||||
| `>` | `lhs > rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` |
|
||||
| `>=` | `lhs >= rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` |
|
||||
| `>` | `> value` | comparison constraint | numeric constraint value | abstract constraint |
|
||||
| `>=` | `>= value` | comparison constraint | numeric constraint value | abstract constraint |
|
||||
| `<` | `< value` | comparison constraint | numeric constraint value | abstract constraint |
|
||||
| `<=` | `<= value` | comparison constraint | numeric constraint value | abstract constraint |
|
||||
| `&&` | `lhs && rhs` | logical | concrete `Bool` | short-circuit AND |
|
||||
| `||` | `lhs || rhs` | logical | concrete `Bool` | short-circuit OR |
|
||||
| `&` | `lhs & rhs` | composition | value / constraint / object | constraint-preserving composition |
|
||||
| `//` | `lhs // rhs` | patch | object / value | right-biased structural patch |
|
||||
| `default` | `base default fallback` | default | abstract value | materialization fallback |
|
||||
| `as` | `narrower as wider` | range refinement | value / constraint / structure | narrower result plus untouched right-only ranges |
|
||||
|
||||
`concrete scalar` は `String`、`Bool`、`Int`、`Float` を指す。
|
||||
|
||||
## 優先順位
|
||||
|
||||
優先順位は高い順に以下である。
|
||||
|
||||
1. 関数呼び出しとフィールド参照
|
||||
2. unary `!` `-`
|
||||
3. `*` `/`
|
||||
4. `+` `-`
|
||||
5. `++`
|
||||
6. `==` `!=` `<` `<=` `>` `>=`
|
||||
7. `&&`
|
||||
8. `||`
|
||||
9. `&`
|
||||
10. `//`
|
||||
11. `default`
|
||||
12. `as`
|
||||
|
||||
同じ優先順位の二項演算子は左結合である。
|
||||
`default` は右結合である。
|
||||
|
||||
## Arithmetic operators
|
||||
|
||||
`+` `-` `*` `/` は具体的な `Int` / `Float` に対する四則演算である。
|
||||
詳しくは [Arithmetic Expression](./expression/arithmetic.md) を参照する。
|
||||
|
||||
## Array concat operator
|
||||
|
||||
`++` は concrete array 同士を連結する演算子である。
|
||||
要素は変換されず、左辺の要素の後に右辺の要素が並ぶ。
|
||||
|
||||
```dcdl
|
||||
["read", "write"] ++ ["admin"]
|
||||
```
|
||||
|
||||
## Logical and comparison operators
|
||||
|
||||
`!` `&&` `||` は concrete `Bool` に対する論理演算である。
|
||||
`&&` と `||` は短絡評価される。
|
||||
|
||||
`==` `!=` は concrete scalar value を比較する。
|
||||
`<` `<=` `>` `>=` は concrete numeric value を比較する。
|
||||
詳しくは [Logical and Comparison Expressions](./expression/logical-and-comparison.md) を参照する。
|
||||
|
||||
## `&`: 制約合成
|
||||
|
||||
`&` は値・制約・構造を合成する演算子である。
|
||||
|
||||
```dcdl
|
||||
A & B
|
||||
```
|
||||
|
||||
基本規則:
|
||||
|
||||
- 両方が制約なら、両方を満たす制約になる。
|
||||
- 制約と具体値なら、具体値が制約を満たす必要がある。
|
||||
- 両方が同じ具体値なら、その値になる。
|
||||
- 両方が異なる具体値なら conflict になる。
|
||||
- 両方が object なら、フィールドごとに合成する。
|
||||
- 同じフィールドが両方にある場合、そのフィールド値を `&` で合成する。
|
||||
- 片方にしかないフィールドは、そのまま結果に保持する。
|
||||
- 矛盾が発生した場合はエラーになる。
|
||||
|
||||
例:
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
NarrowedPort = Port & > 443;
|
||||
|
||||
MyConfig = {
|
||||
port = NarrowedPort default 8080;
|
||||
};
|
||||
|
||||
Config = MyConfig & {
|
||||
port = 8000;
|
||||
};
|
||||
```
|
||||
|
||||
`Config.port` は概念的には以下になる。
|
||||
|
||||
```text
|
||||
NarrowedPort & 8000
|
||||
```
|
||||
|
||||
`8000` は `NarrowedPort` を満たすため成功する。
|
||||
|
||||
一方、以下は失敗する。
|
||||
|
||||
```dcdl
|
||||
BadConfig = MyConfig & {
|
||||
port = 80;
|
||||
};
|
||||
```
|
||||
|
||||
`80` は `> 443` を満たさないためである。
|
||||
|
||||
## `//`: patch 合成
|
||||
|
||||
`//` は右辺優先の構造的 patch 演算子である。
|
||||
`&` が制約を保った合成であるのに対し、`//` は設定やスキーマを上書き・変更するために使う。
|
||||
|
||||
```dcdl
|
||||
A // B
|
||||
```
|
||||
|
||||
基本規則:
|
||||
|
||||
- 両方が object なら、フィールドごとに再帰的に patch する。
|
||||
- 同じフィールドが object/object なら、さらに再帰的に patch する。
|
||||
- 同じフィールドが object/object 以外なら、右辺で置き換える。
|
||||
- 左辺にしかないフィールドは保持する。
|
||||
- 右辺にしかないフィールドは追加する。
|
||||
- 配列はデフォルトでは右辺で置き換える。
|
||||
- 関数値はデフォルトでは右辺で置き換える。
|
||||
|
||||
つまり `//` は shallow merge ではなく deep patch とする。
|
||||
|
||||
```dcdl
|
||||
Base = {
|
||||
feature_hoge = {
|
||||
enable = Bool default true;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
};
|
||||
|
||||
Patched = Base // {
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`Patched` は以下に相当する。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
ドットパスを使うと以下のようにも書ける。
|
||||
|
||||
```dcdl
|
||||
Patched = Base // {
|
||||
feature_hoge.enable = false;
|
||||
};
|
||||
```
|
||||
|
||||
## object 全体の置換
|
||||
|
||||
`//` では object/object は常に deep patch される。
|
||||
object field 全体を特別に置き換えるための `replace(...)` 構文や組み込み関数は core には含めない。
|
||||
|
||||
object 全体を別構造にしたい場合は、patch 対象より外側で値を作り直す。
|
||||
|
||||
## `as`: range refinement
|
||||
|
||||
`as` は左辺が右辺より具体的で狭い範囲であることを確認する、非対称な絞り込み演算子である。
|
||||
|
||||
```dcdl
|
||||
Refined = {
|
||||
port = 8000;
|
||||
} as {
|
||||
port = Int & >= 1 & <= 65535;
|
||||
host = String;
|
||||
enabled = Bool default true;
|
||||
};
|
||||
```
|
||||
|
||||
`port` は左辺の `8000` に具体化される。
|
||||
右辺にしかない `host` と `enabled` は abstract のまま結果へ残る。default の有無は、右辺だけの field を保持するかどうかに影響しない。
|
||||
`as` 自体は default を選択せず、後から結果を materialize した場合だけ通常の default 規則が働く。
|
||||
|
||||
左辺にしかない object field は右辺の field domain 外なのでエラーになる。
|
||||
両側にある nested object、array constraint、map constraint は同じ規則で再帰的に絞り込む。
|
||||
|
||||
abstract range 同士も包含を確認できる。
|
||||
|
||||
```dcdl
|
||||
Int & > 10 as Int & > 0 # 成功し、Int & > 10 を返す
|
||||
Int as Int & > 0 # 失敗
|
||||
```
|
||||
|
||||
`&` は対称な合成であり、片方だけにある object field を保持する。
|
||||
したがって、左右に方向を持つ絞り込みを `&` で代用しない。
|
||||
詳細は [Range Refinement](./expression/ascription.md) を参照する。
|
||||
|
||||
## `&`、`//`、`as` の使い分け
|
||||
|
||||
`&` は制約や部分構造を失わず、対称に合成する。
|
||||
|
||||
```dcdl
|
||||
Combined = MyConfig & {
|
||||
port = 8000;
|
||||
};
|
||||
```
|
||||
|
||||
`//` は既存構造の上書きや変形に使う。
|
||||
|
||||
```dcdl
|
||||
ModifiedSchema = MyConfig // {
|
||||
port = Int default 9000;
|
||||
};
|
||||
```
|
||||
|
||||
`//` は右辺優先の patch であり、左辺の制約を常に保持するとは限らない。
|
||||
制約を保持したい場合は `&` を使う。
|
||||
|
||||
左辺が右辺より狭いことを検証しながら合成する場合は `as` を使う。
|
||||
|
||||
```dcdl
|
||||
RefinedConfig = NarrowConfig as WideConfig;
|
||||
```
|
||||
@@ -0,0 +1,129 @@
|
||||
# 構文と字句
|
||||
|
||||
この章では、表層構文の方針をまとめる。
|
||||
厳密な構文は [Grammar](./grammar.md) の EBNF を参照する。
|
||||
|
||||
## 言語名と拡張子
|
||||
|
||||
プロジェクト名は **Decodal** とする。
|
||||
正式な説明名は **Deferred Constraint Data Language**、略称は **DCDL** とする。
|
||||
ファイル拡張子は `.dcdl` とする。
|
||||
|
||||
```text
|
||||
config.dcdl
|
||||
schema.dcdl
|
||||
service.dcdl
|
||||
```
|
||||
|
||||
## Module source
|
||||
|
||||
ファイル全体は単一の式として書ける。
|
||||
また、top-level に field 定義列を書いた場合は、暗黙の object として扱う。
|
||||
|
||||
```dcdl
|
||||
host = String;
|
||||
port = Int default 8080;
|
||||
```
|
||||
|
||||
上の source は以下と同じ意味である。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
host = String;
|
||||
port = Int default 8080;
|
||||
}
|
||||
```
|
||||
|
||||
## コメント
|
||||
|
||||
コメントは `#` から行末までとする。
|
||||
|
||||
```dcdl
|
||||
# comment
|
||||
host = "127.0.0.1"; # trailing comment
|
||||
```
|
||||
|
||||
## セミコロン
|
||||
|
||||
オブジェクトフィールド、let 束縛、match 分岐はセミコロンで区切る。
|
||||
末尾セミコロンは許可する。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
host = "127.0.0.1";
|
||||
port = 8000;
|
||||
}
|
||||
```
|
||||
|
||||
## 識別子
|
||||
|
||||
識別子は ASCII 英字で始まり、ASCII 英数字または `_` を続けられる。
|
||||
命名規則には `lower_snake`、`lowerCamel`、`UpperCamel` を使用できる。
|
||||
|
||||
```dcdl
|
||||
my_config
|
||||
mkConfig
|
||||
IPv4Address
|
||||
```
|
||||
|
||||
## パス参照
|
||||
|
||||
ドットによるフィールド参照を許可する。
|
||||
|
||||
```dcdl
|
||||
config.host
|
||||
config.feature_hoge.enable
|
||||
```
|
||||
|
||||
オブジェクト内では、ドットパスによるフィールド定義も許可する。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge.enable = false;
|
||||
}
|
||||
```
|
||||
|
||||
これは以下と同じ構造を表す。
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 予約語
|
||||
|
||||
以下は予約語として扱う。
|
||||
|
||||
```text
|
||||
let
|
||||
in
|
||||
match
|
||||
import
|
||||
default
|
||||
as
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
## 演算子
|
||||
|
||||
主要な演算子は以下である。
|
||||
|
||||
```text
|
||||
+ - * / 四則演算
|
||||
++ 配列結合
|
||||
! && || 論理演算
|
||||
== != < <= > >= 比較式
|
||||
& 制約合成
|
||||
// patch 合成
|
||||
default fallback 指定
|
||||
as 左辺から右辺への範囲包含確認と絞り込み
|
||||
=> 関数
|
||||
. フィールド参照 / ドットパス定義
|
||||
... 配列または連想配列の値制約
|
||||
```
|
||||
|
||||
演算子の優先順位は [合成演算子](./operators.md) で定義する。
|
||||
@@ -0,0 +1,19 @@
|
||||
# Bool
|
||||
|
||||
`Bool` は真偽値を表す primitive type constraint である。
|
||||
|
||||
## 例
|
||||
|
||||
```dcdl
|
||||
enable = Bool default true;
|
||||
disable = Bool default false;
|
||||
```
|
||||
|
||||
## リテラル
|
||||
|
||||
`Bool` が受け入れるリテラルは以下である。
|
||||
|
||||
```dcdl
|
||||
true
|
||||
false
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
# Float
|
||||
|
||||
`Float` は浮動小数値を表す primitive type constraint である。
|
||||
|
||||
## 例
|
||||
|
||||
```dcdl
|
||||
ratio = Float;
|
||||
threshold = Float default 0.5;
|
||||
```
|
||||
|
||||
## Int との関係
|
||||
|
||||
`Int` と `Float` は primitive type constraint としては別の型である。
|
||||
`Float` constraint は concrete `Float` を要求し、`Int` constraint は concrete `Int` を要求する。
|
||||
|
||||
数値演算や比較式では `Int` と `Float` を同じ numeric value として扱える。
|
||||
混在した四則演算の結果は `Float` になる。
|
||||
@@ -0,0 +1,20 @@
|
||||
# Value
|
||||
|
||||
この章では、言語が扱う値の分類を定義する。
|
||||
|
||||
primitive type は、通常のデータ値ではなく、値が満たすべき組み込み制約として扱う。
|
||||
|
||||
```dcdl
|
||||
name = String;
|
||||
retry = Int default 3;
|
||||
ratio = Float;
|
||||
enable = Bool default true;
|
||||
tags = [...String];
|
||||
```
|
||||
|
||||
primitive type は `String`、`Int`、`Float`、`Bool` である。
|
||||
`Unknown` はprimitive typeではなく、すべてのDecodal値を含む最上位の抽象rangeである。具体値またはdefaultがない `Unknown` はmaterializeできない。
|
||||
配列は primitive type ではなく、必須の要素制約を持つ `[...T]` で表現する。
|
||||
各型の個別仕様へのリンクは [Manual Index](../../index.md) に集約する。
|
||||
|
||||
primitive type と制約合成の詳細は [制約と default](../constraints-and-defaults.md) も参照する。
|
||||
@@ -0,0 +1,18 @@
|
||||
# Int
|
||||
|
||||
`Int` は整数値を表す primitive type constraint である。
|
||||
|
||||
## 例
|
||||
|
||||
```dcdl
|
||||
retry = Int default 3;
|
||||
port = Int & >= 1 & <= 65535;
|
||||
```
|
||||
|
||||
## 制約合成
|
||||
|
||||
数値比較制約と合成できる。
|
||||
|
||||
```dcdl
|
||||
NarrowedPort = Int & >= 1 & <= 65535 & > 443;
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
# String
|
||||
|
||||
`String` は文字列値を表す primitive type constraint である。
|
||||
|
||||
文字列リテラルは `"` で囲む。
|
||||
|
||||
```dcdl
|
||||
"hello"
|
||||
"line 1\nline 2"
|
||||
```
|
||||
|
||||
文字列内の `$` や `{}` に特別な意味はなく、文字列補間は行わない。
|
||||
|
||||
## 例
|
||||
|
||||
```dcdl
|
||||
name = String;
|
||||
greeting = String default "hello";
|
||||
```
|
||||
|
||||
## 制約合成
|
||||
|
||||
`String` は文字列制約と合成できる。
|
||||
|
||||
```dcdl
|
||||
message = String & /Hello! .*/;
|
||||
```
|
||||
|
||||
正規表現制約の検証は Rust crate の `regex` feature で有効化される。
|
||||
feature が無効な場合、正規表現制約は具体 string に対して検証できず diagnostic になる。
|
||||
Reference in New Issue
Block a user