Decodal/doc/manual/souce/language/operators.md
2026-06-18 23:42:53 +09:00

210 lines
6.1 KiB
Markdown

# 演算子
この章では、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` | 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 |
`concrete scalar``String`、`Bool`、`Int`、`Float` を指す。
`++` による array concat は未導入である。
## 優先順位
優先順位は高い順に以下である。
1. 関数呼び出しとフィールド参照
2. unary `!` `-`
3. `*` `/`
4. `+` `-`
5. `==` `!=` `<` `<=` `>` `>=`
6. `&&`
7. `||`
8. `&`
9. `//`
10. `default`
同じ優先順位の二項演算子は左結合である。
`default` は右結合である。
## Arithmetic operators
`+` `-` `*` `/` は具体的な `Int` / `Float` に対する四則演算である。
詳しくは [Arithmetic Expression](./expression/arithmetic.md) を参照する。
## 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 全体の置換
`//` が deep patch である場合、object 全体を置き換えたいときの escape hatch が必要になる。
候補として、`replace(...)` を組み込み関数として提供する。
```dcdl
Replaced = Base // {
feature_hoge = replace({
enable = false;
});
};
```
この場合、`feature_hoge` は再帰 patch されず、右辺の object に丸ごと置き換わる。
## `&` と `//` の使い分け
`&` は制約を満たす具体化に使う。
```dcdl
ValidConfig = MyConfig & {
port = 8000;
};
```
`//` は既存構造の上書きや変形に使う。
```dcdl
ModifiedSchema = MyConfig // {
port = Int default 9000;
};
```
`//` は右辺優先の patch であり、左辺の制約を常に保持するとは限らない。
制約を保持したい場合は `&` を使う。