9.4 KiB
WIP IDL
WIP IDLは、Operationのinput / outputを記述するための小さなinterface definition languageである。
WIP IDLのsourceそのものをschemaのcanonicalな交換形式とする。HostとClientの間でJSON SchemaやIDLを変換したJSON ASTを交換することは前提としない。Clientはsourceをparseして内部表現を構築し、必要に応じてAI Tool schemaやGUI formへ投影する。
この文書では、WIP IDLの初期文法と型を定義する。
設計方針
- Operation境界の型だけを表現する。
- 文法を小さく保ち、一般的なschema validation languageにはしない。
- Operationのinputは名前付きfieldを持つrecordとする。
- Wire上の値表現はTransport Bindingが定める。
- JSON SchemaはWIPの交換形式にせず、必要なClientが生成する。
- Worldspaceのエントリは、そのpathを表す
entry型として扱う。
Source
WIP IDL sourceはUTF-8 textとする。ASCII space、tab、LFによる空白は、tokenを分離する場合を除いて意味を持たない。
//から行末まではcommentとし、parserは無視する。
IdentifierはASCIIの英字または_で始まり、以降にASCIIの英数字または_を含められる。
identifier = (ALPHA | "_") { ALPHA | DIGIT | "_" };
Keyword、primitive type名、Worldspace固有型名はidentifierとして使用できない。
Named typeにはUpperCamelCase、Operation、field、enum case、union caseにはsnake_caseを使用する。
Grammar
以下のEBNFは空白とcommentを省略して示す。
document = { declaration } ;
declaration = type-declaration | operation-declaration ;
type-declaration = "type", type-name, "=", type-definition, ";" ;
type-definition = type-expression | enum-type | union-type ;
operation-declaration =
"operation", identifier, "{",
"input", ":", type-expression, ";",
"output", ":", type-expression, ";",
"}" ;
type-expression =
primitive-type
| worldspace-type
| type-name
| record-type
| list-type ;
record-type =
"{", [ record-field, { ",", record-field }, [ "," ] ], "}" ;
record-field = identifier, [ "?" ], ":", type-expression ;
list-type = "[", type-expression, "]" ;
enum-type =
"enum", "{", identifier, { ",", identifier }, [ "," ], "}" ;
union-type =
"union", "{", union-case, { ",", union-case }, [ "," ], "}" ;
union-case = identifier, [ "(", type-expression, ")" ] ;
primitive-type =
"unit"
| "boolean"
| "integer"
| "number"
| "string"
| "bytes"
| "json" ;
worldspace-type = "entry" ;
type-name = identifier ;
同じdocument内でtype名またはOperation名を重複して宣言してはならない。Named typeは同じdocument内で宣言されたtypeを参照する。
Primitive types
| Type | 意味 | WIP over HTTPSでの表現 | 制約 |
|---|---|---|---|
unit |
値を持たない | JSON null |
Optional fieldの省略とは区別する |
boolean |
真偽値 | JSON boolean | — |
integer |
符号付き整数 | 小数部を持たないJSON number | -9007199254740991以上、9007199254740991以下 |
number |
IEEE 754 binary64値 | JSON number | NaN、positive infinity、negative infinityは不可 |
string |
Unicode string | JSON string | — |
bytes |
任意のoctet列 | RFC 4648 Section 4のbase64を格納したJSON string | paddingを含む |
json |
opaqueな任意のJSON value | JSON value | Clientは内部構造をschemaとして解釈しない |
integerの範囲は、異なる言語のClient間で正確に交換できる範囲に制限している。この範囲を超える整数はintegerとしてencodeしない。i64、u64、bigint等の追加型とlosslessなwire表現は将来の拡張として扱う。
jsonは外部APIの応答など構造を事前に固定できない値のためのescape hatchであり、通常のOperation interfaceではより具体的な型を優先する。
Composite types
| Type | 構文 | 意味 | WIP over HTTPSでの表現 |
|---|---|---|---|
| Record | { field: Type } |
名前付きfieldの集合 | JSON object |
| List | [Type] |
同じ型の順序付き値 | JSON array |
| Named type | type Name = Type; |
型への名前付け | 参照先と同じ表現 |
| Enum | enum { first, second } |
payloadを持たないcaseの集合 | case名のJSON string |
| Union | union { case(Type), empty } |
discriminatorを持つcaseの集合 | $caseを持つJSON object |
Record
Recordは名前付きfieldの集合である。
type Query = {
text: string,
limit?: integer,
};
?を持たないfieldはrequired、?を持つfieldはoptionalである。Optionalはrecord fieldにのみ適用し、一般化しない。
WIP over HTTPSではrecordをJSON objectとしてencodeする。Optional fieldに値がない場合はfield自体を省略する。FieldをJSON nullにすることは省略と同じ意味ではなく、そのfieldの型がunitまたはjsonとしてnullを許す場合に限る。
宣言されていないfieldをrecordに含めてはならない。
List
Listは同じ型の0個以上の順序付き値を表す。
[entry]
WIP over HTTPSではJSON arrayとしてencodeする。
Named type
type宣言によって型へ名前を付けられる。
type IssueList = [entry];
Aliasを参照する値のwire表現は、参照先の型と同じである。
Enum
Enumはpayloadを持たないcaseの集合である。
type Relation = enum {
parent,
sibling,
related,
};
WIP over HTTPSではcase名をJSON stringとしてencodeする。
"sibling"
Union
Unionはdiscriminatorを持つcaseの集合である。Caseは0個または1個のpayloadを持つ。
type LookupResult = union {
found(entry),
not_found,
ambiguous([entry]),
};
WIP over HTTPSでは$case discriminatorを持つJSON objectとしてencodeする。Payloadを持つcaseはvalue fieldに値を格納する。
{
"$case": "found",
"value": "/items/123"
}
Payloadを持たないcaseにはvalueを含めない。
{
"$case": "not_found"
}
Worldspace type
| Type | 意味 | WIP over HTTPSでの表現 |
|---|---|---|
entry |
同じWorldspace内のエントリを指すcanonical absolute path | JSON string |
"/items/123"
Wire上では通常のstringと同じ表現だが、WIP IDL上でentryと宣言された位置にある値だけをClientがエントリとして認識する。通常のstringやopaqueなjsonに含まれるpathらしい文字列を、Clientがエントリとして推測してはならない。
Operation Resultからentryを発見したClientは、そのpathを一時的に利用し、必要に応じてfetchまたはfetch_treeする。複数のpathを効率的に取得するためのbatchingや、取得結果をinvoke responseへ同梱する最適化は、entry型のwire表現とは分離する。
Operationが返したpathは、Operationの実行対象の子に限定されない。親、兄弟、関連Objectなど、Worldspace内の任意のエントリを指してよい。Operationの実行対象と返されたpathの間に、暗黙のTree edgeや意味的関係を推論してはならない。
fetchが返すエントリ公開情報と、fetch_treeが返すindexableなtreeはCore Protocolのresponseであり、WIP IDLの値型としては定義しない。
Operation declarations
Operationはinputとoutputをそれぞれ一つ宣言する。
type Relation = enum {
parent,
sibling,
related,
};
operation get_related_item {
input: {
relation: Relation,
limit?: integer,
};
output: {
items: [entry],
next_cursor?: string,
};
}
Operationのinputは、直接またはNamed typeを介してrecordへ解決されなければならない。位置引数は定義しない。
引数を持たないOperationは空のrecordをinputにする。
operation refresh {
input: {};
output: unit;
}
Outputには任意のtype expressionまたはNamed typeを使用できる。ただし、将来fieldを追加する可能性があるOperationではrecord outputを推奨する。
Operationのnameやdescriptionなど、input / output型以外のinterface metadataとの対応付けはOperation schemaで定義する。
Schema exchange
HostはOperation interfaceとともに、次のschema情報をClientへ提供する。
| 項目 | 内容 |
|---|---|
| Language identifier | 初期versionはwip-idl/1 |
| Source | WIP IDL source、またはsourceを取得するための参照 |
| Digest | Cacheと同一性確認に利用するdigest |
交換されるschemaの正本はWIP IDL sourceである。HostはJSON SchemaやIDL ASTを併記する必要はない。
ClientはIDLを内部ASTへparseし、必要に応じてAI provider用JSON Schema、GUI form、言語固有の型などを生成できる。
Sourceのcanonicalization規則、digest algorithm、参照方法の詳細はTransport Bindingで定義する。
未解決事項
- Sourceにdescriptionやdocumentation commentを含めるか。
- String length、numeric range、pattern等のconstraintを導入するか。
- Recursive named typeを許可するか。
i64、u64、bigint、decimal等を追加するか。- Sourceのcanonicalizationとdigestの厳密な計算方法。
entryが参照するpathのcanonicalization規則。