言語リファレンス
wirespec 言語の完全なリファレンスです。入門的な解説は 言語ツアー を参照してください。
型
プリミティブ整数型
| 型 | ビット幅 | 符号 | C マッピング |
|---|---|---|---|
u8 | 8 ビット | 符号なし | uint8_t |
u16 | 16 ビット | 符号なし | uint16_t |
u24 | 24 ビット | 符号なし | uint32_t(下位 24 ビット) |
u32 | 32 ビット | 符号なし | uint32_t |
u64 | 64 ビット | 符号なし | uint64_t |
i8 | 8 ビット | 符号あり | int8_t |
i16 | 16 ビット | 符号あり | int16_t |
i32 | 32 ビット | 符号あり | int32_t |
i64 | 64 ビット | 符号あり | int64_t |
u24 は 3 バイトの符号なし整数で、uint32_t に格納されます。パース時は 3 バイト読み取り、シリアライズ時は 3 バイト書き込みます。TLS レコードヘッダなどで使われます。
明示的エンディアンネス整数型
| 型 | ビット幅 | エンディアンネス | C マッピング |
|---|---|---|---|
u16be | 16 ビット | ビッグエンディアン | uint16_t |
u16le | 16 ビット | リトルエンディアン | uint16_t |
u24be | 24 ビット | ビッグエンディアン | uint32_t |
u24le | 24 ビット | リトルエンディアン | uint32_t |
u32be | 32 ビット | ビッグエンディアン | uint32_t |
u32le | 32 ビット | リトルエンディアン | uint32_t |
u64be | 64 ビット | ビッグエンディアン | uint64_t |
u64le | 64 ビット | リトルエンディアン | uint64_t |
i16be | 16 ビット | ビッグエンディアン | int16_t |
i16le | 16 ビット | リトルエンディアン | int16_t |
i32be | 32 ビット | ビッグエンディアン | int32_t |
i32le | 32 ビット | リトルエンディアン | int32_t |
i64be | 64 ビット | ビッグエンディアン | int64_t |
i64le | 64 ビット | リトルエンディアン | int64_t |
明示的エンディアン型は @endian モジュールアノテーションの設定によらず、常に宣言されたエンディアンで読み書きします。1 つの構造体内でエンディアンを混在させたい場合に使います。
ビット型
| 型 | ビット幅 | C マッピング |
|---|---|---|
bit | 1 ビット | BitGroup の一部 → uint8_t / uint16_t / uint32_t |
bits[N] | N ビット | BitGroup の一部 → uint8_t / uint16_t / uint32_t |
bit は bits[1] と等価です。連続する bit / bits[N] フィールドは自動的に 1 回の読み取り操作にまとめられます。詳細は BitGroup パッキング を参照してください。
バイト型
| 構文 | セマンティクス | C マッピング |
|---|---|---|
bytes[N] | 固定 N バイト | wirespec_bytes_t(ゼロコピー) |
bytes[length: EXPR] | EXPR バイト(EXPR は整数類似型が必須) | wirespec_bytes_t |
bytes[remaining] | 現在のスコープの残りバイトすべて | wirespec_bytes_t |
bytes[length_or_remaining: EXPR] | EXPR が Some の場合はその値、None の場合は残りを消費 | wirespec_bytes_t |
いずれの形式も、入力バッファへのゼロコピービューである wirespec_bytes_t を生成します。
typedef struct { const uint8_t *ptr; size_t len; } wirespec_bytes_t;ヒープアロケーションは発生しません。ポインタは元の入力バッファを直接指します。
セマンティック型
| 型 | 種類 | 使用可能な位置 | C マッピング |
|---|---|---|---|
bool | セマンティック型(ワイヤ型ではない) | let バインディング、guard 条件 | bool |
bool はワイヤフィールドには使えません。let バインディングにおける比較・論理式の結果型として使います。型チェッカーは bool を予約済みの組み込み名として扱うため、ユーザーが bool という名前で型やフィールドを定義することはできません。
bytes スペックバリアント
bytes[...] には 4 つの形式があり、一般的なプロトコルパターンを網羅します。
bytes[N] — 固定長
ちょうど N バイトを読み書きします。N はコンパイル時定数(整数リテラル)です。
reset_token: bytes[16], # 常に 16 バイト
random: bytes[32], # 常に 32 バイトbytes[length: EXPR] — 長さプレフィックス付き
EXPR で指定されたバイト数を読み書きします。EXPR はパース/シリアライズ時に評価され、整数類似の値(先行フィールドまたはその算術式)でなければなりません。
packet MqttString {
length: u16,
data: bytes[length], # 正確に 'length' バイトを読み取る
}
# 先行フィールドへの算術演算も許可
data: bytes[length: length - 8], # ヘッダオーバーヘッドを引くEXPR は整数類似型(u8、u16、u32、u64、VarInt 等のセマンティック整数コーデック)でなければなりません。それ以外の型を使うとコンパイルエラーです。
bytes[remaining] — スコープ残り
現在のスコープの残りバイトをすべて消費します。スコープ内の最後のワイヤフィールドである必要があります。後に let や require を書くのは問題ありませんが、ワイヤフィールドは続けられません。
_ => Unknown { data: bytes[remaining] }, # キャッチオールブランチ
0x0b => ReadRsp { value: bytes[remaining] },bytes[length_or_remaining: EXPR] — オプション長さ
EXPR は Option[T](T は整数類似型)でなければなりません。EXPR が非 null ならそのバイト数だけ読み取り、null ならスコープの残りをすべて消費します。
0x08..=0x0f => Stream {
length_raw: if frame_type & 0x02 { VarInt },
data: bytes[length_or_remaining: length_raw],
# ↑ length_raw が存在する場合はその値を使用、そうでなければ残りを消費
},非オプションの整数を length_or_remaining に渡すとコンパイルエラーです。その場合は bytes[length: EXPR] を使ってください。
継続ビット VarInt
varint { } ブロックは、各バイトの継続フラグで長さが決まる可変長整数型を定義します。MQTT Remaining Length、Protocol Buffers varint、LEB128 などに対応します。
type MqttLength = varint {
continuation_bit: msb, # 継続フラグのビット位置
value_bits: 7, # 1 バイトあたりのデータビット数
max_bytes: 4, # 最大エンコードバイト数(超過 → エラー)
byte_order: little, # 下位グループが先
}パラメータ
| パラメータ | 値 | 説明 |
|---|---|---|
continuation_bit | msb または lsb | 各バイト内の継続フラグのビット位置 |
value_bits | 正の整数 | 1 バイトあたりのデータビット数 |
max_bytes | 正の整数 | WIRESPEC_ERR_OVERFLOW が返されるまでの最大バイト数 |
byte_order | little または big | バイト重要度順:little = 下位グループが先(MQTT、Protobuf);big = 上位グループが先 |
max_bytes バイトを読み終えても継続ビットがセットされたままの場合、パーサーは WIRESPEC_ERR_OVERFLOW を返します。
C では、最大値を格納できる最小の符号なし整数型にデコードされます。MqttLength(最大 4 バイト、28 データビット)の場合は uint32_t です。
式言語
優先順位テーブル
結合力の低い順に並べています。同じ行の演算子は同じ優先順位です。
| レベル | 演算子 | 結合性 | 備考 |
|---|---|---|---|
| 1 | ?? | 左 | コアレス:Option[T] ?? T → T |
| 2 | or | 左 | 論理 OR |
| 3 | and | 左 | 論理 AND |
| 4 | == != < <= > >= | なし(非結合) | 比較 |
| 5 | | | 左 | ビット OR |
| 6 | ^ | 左 | ビット XOR |
| 7 | & | 左 | ビット AND |
| 8 | << >> | 左 | ビットシフト |
| 9 | + - | 左 | 加算・減算 |
| 10 | * / % | 左 | 乗算・除算・剰余 |
| 11 | ! - | 右 | 単項論理 NOT・単項否定 |
| 12 | . [] [..] | 左 | メンバアクセス・インデックス・スライス |
| 13 | () リテラル 名前 | — | 一次式 |
ビット演算子と比較演算子の優先順位
wirespec では、ビット演算子(& | ^)は比較演算子(== != < <= > >=)より強く結合します。これは C および Java とは逆です。
| 式 | wirespec の解析 | C の解析 |
|---|---|---|
a & mask == 0 | (a & mask) == 0 | a & (mask == 0) |
flags | 0x04 != 0 | (flags | 0x04) != 0 | flags | (0x04 != 0) |
wirespec のルールはプログラマの意図に沿っています。ビットマスクのテストでは、マスク結果に対して比較を行いたいケースがほとんどです。C のルールはバグの温床として有名で、実質的に毎回余分な括弧が必要になります。
?? コアレス演算子
?? はフォールバック値を指定して Option[T] をアンラップします。
Option[T] ?? T → Tlet offset: u64 = offset_raw ?? 0, # offset_raw が存在しない場合は 0offset_raw が存在すればその値を返し、null なら右オペランドを返します。右オペランドの型は T と一致している必要があります。
範囲演算子
| 演算子 | 意味 | 文脈 |
|---|---|---|
..= | 閉区間 [start, end] | パターンマッチングのみ |
.. | 半開区間 [start, end) | スライス式・量化子 |
0x02..=0x03 => Ack { ... }, # 2 と 3(両端含む)にマッチ
paths[0..active_path_count], # インデックス 0 から count-1 までリテラル
| 形式 | 例 | 型 |
|---|---|---|
| 十進整数 | 42、255 | 整数 |
| 十六進整数 | 0x06、0x1c | 整数 |
| 二進整数 | 0b00、0b11 | 整数 |
| 文字列 | "hello" | 文字列(アノテーションのみ) |
| 真偽値 | true、false | bool |
| Null | null | Option の null リテラル |
フィールドの種類
概要テーブル
| 種類 | 構文 | ワイヤ上? | C 構造体? | 備考 |
|---|---|---|---|---|
| ワイヤ | name: T | Yes | Yes | パース中にバイトを消費 |
| オプション | name: if COND { T } | 条件付き | Yes | C では bool has_name; T name; |
| 導出 | let name: T = EXPR | No | Yes | 先行フィールドから計算 |
| 検証 | require EXPR | No | No | 実行時チェック;エラー → WIRESPEC_ERR_CONSTRAINT |
ワイヤフィールド
ワイヤフィールドは宣言順に入力からバイトを消費します。
src_port: u16,
dst_port: u16,
length: u16,
checksum: u16,オプションフィールド
COND が true のときだけワイヤ上に存在するフィールドです。COND では、先に宣言されたワイヤフィールドや let フィールドを参照できます。
ecn_counts: if frame_type == 0x03 { EcnCounts },
offset_raw: if frame_type & 0x04 { VarInt },
packet_id: if qos > 0 { u16 },C では:
bool has_ecn_counts;
ecn_counts_t ecn_counts;後続の式でオプションフィールドの値を使うには、同じ条件でガードするか ?? でデフォルトを指定してください。
導出フィールド(let)
let フィールドはバイトを消費せず、先行フィールドから値を計算して構造体に格納します。
let offset: u64 = offset_raw ?? 0,
let fin: bool = (frame_type & 0x01) != 0,
let qos: u8 = (type_and_flags & 0x06) >> 1,let では同スコープのワイヤフィールドや先行する let フィールドを参照できます。bool は let でのみ使えるセマンティック型で、ワイヤフィールドには使えません。
シリアライズ時はワイヤフィールドから式を再計算するため、構造体に格納された let の値は使われません。
検証(require)
require EXPR はパース時の実行時チェックです。EXPR が false なら WIRESPEC_ERR_CONSTRAINT を返します。
require length >= 8,
require length <= MAX_CID_LENGTH,
require data_offset >= 5,
require type_and_flags & 0x0F == 0x02,require はフィールド間のどこにでも書けます。先に宣言されたワイヤフィールドや let フィールドを参照でき、C 構造体には現れません。
コンパイル時アサーション(static_assert)
static_assert EXPR はコンパイル時に評価され、失敗するとコード生成前にエラーになります。
const MAX_CID_LENGTH: u8 = 20
static_assert MAX_CID_LENGTH <= 255フィールドの可視性と順序
packet、frame ブランチ、capsule 内のフィールドは、同スコープの後続フィールドから宣言順(上から下)に参照できます。
bytes[length: X]は先に宣言されたフィールドXを参照可能。if CONDは先に宣言されたフィールドを参照可能。letは先に宣言されたワイヤフィールドまたは先行するletを参照可能。requireは先に宣言されたワイヤフィールドやletを参照可能。- 自身または後方のフィールドへの参照はコンパイルエラー。
スコープ規則
bytes[remaining] と [T; fill] は現在のスコープの残りをすべて消費するため、スコープ内の最後のワイヤフィールドでなければなりません。let や require は後に続けられます。
スコープ境界
以下の構成要素はそれぞれ独立したスコープを形成します。
| 構成要素 | スコープ |
|---|---|
packet Foo { ... } | { ... } の本体全体 |
frame の各ブランチ | 各 => Name { ... } の本体 |
capsule ヘッダフィールド | within 節より前のフィールド |
capsule within の各ブランチ | 各 => Name { ... } の本体 |
if COND { T } | 条件内部の単一フィールド T |
bytes[remaining] が合法な場所
# ブランチ本体 — OK
frame F = match tag: u8 {
0 => A { data: bytes[remaining] },
1 => B { x: u8, data: bytes[remaining] },
}
# capsule within ブランチ — OK
capsule C {
type: u8, length: u16,
payload: match type within length {
0 => D { entries: [Entry; fill] },
_ => Unknown { data: bytes[remaining] },
},
}bytes[remaining] が不正な場所
# NG: remaining の後にワイヤフィールドが続く
packet Bad {
data: bytes[remaining], # コンパイルエラー:ワイヤフィールドが後続
trailer: u8,
}整数類似型
配列カウント、バイト長(bytes[length: ...])、スコープ長(within EXPR)で使える型です。
- プリミティブ:
u8、u16、u24、u32、u64 - セマンティック整数コーデック:
varint { }で定義したユーザー型(VarInt、MqttLength等)。基礎となる符号なし整数に解決されます。
符号あり整数(i8 等)、bool、bytes、複合型、非整数の列挙型、非整数の Option は整数類似型ではなく、配列カウントやバイト長に使うとコンパイルエラーです。
BitGroup パッキング
連続する bit / bits[N] フィールドは自動的に 1 回のワイヤ読み取りにまとめられます。
- グループ内のビット合計は 8 の倍数(整数バイト)でなければならない(コンパイル時チェック)。
@endian big(デフォルト):先に宣言したフィールドが上位ビットを占める。@endian little:先に宣言したフィールドが下位ビットを占める。- 各フィールドは生成 C コードでシフト+マスクにより抽出される。
# examples/ip/ipv4.wspec より(ビッグエンディアン)
packet IPv4Header {
version: bits[4], # バイト 0 の上位 4 ビット
ihl: bits[4], # バイト 0 の下位 4 ビット
dscp: bits[6], # バイト 1 の上位 6 ビット
ecn: bits[2], # バイト 1 の下位 2 ビット
total_length: u16, # 別途の u16 フィールド
...
}生成される C コード:
uint8_t _b0 = buf[pos++];
out->version = (_b0 >> 4) & 0x0f;
out->ihl = (_b0 >> 0) & 0x0f;
uint8_t _b1 = buf[pos++];
out->dscp = (_b1 >> 2) & 0x3f;
out->ecn = (_b1 >> 0) & 0x03;BitGroup はビット型以外のフィールドが出現するか、構造体の末尾で区切られます。
予約済み識別子
以下の名前は wirespec の型チェッカーまたはランタイムで特別な意味を持ちます。ユーザーコードでこれらの名前の型・フィールド・変数・イベントを定義することはできません。
| 名前 | 種類 | 許可される文脈 |
|---|---|---|
bool | 組み込みセマンティック型 | let バインディング型、guard 式の結果 |
null | 組み込みリテラル値 | Option[T] 比較、?? の右オペランド |
fill | キーワードおよび組み込み関数 | 配列サイズ式([T; fill]);イニシャライザ式(fill(value, count)) |
remaining | キーワード | bytes_spec のみ:bytes[remaining] |
in_state | 組み込み述語 | 状態機械での guard、verify、all() |
all | 組み込み量化子 | 状態機械での guard と verify |
child_state_changed | 内部イベント | delegate によって発行される;ユーザー定義イベント名として使用不可 |
src | 遷移バインディング | 状態機械遷移内の guard および action ブロック |
dst | 遷移バインディング | 状態機械遷移内の action ブロック |
これらの識別子で型・定数・フィールド・イベントを定義しようとするとコンパイルエラーです。
トップレベル宣言
module
module quic.varint
module ble.att
module mqtt現在のファイルのモジュール識別子を宣言します。ドット区切りの名前はファイルパスに対応し、quic.varint はインクルードパス上の quic/varint.wspec(または quic/varint.wspec)に解決されます。1 ファイルにつき module 宣言は最大 1 つで、他の宣言より前に書く必要があります。
import
import quic.varint.VarInt
import quic.varint # モジュール自体をインポート別モジュールの名前を現在のファイルに導入します。循環インポートはコンパイルエラー、相対インポートは非対応です。
const
const MAX_CID_LENGTH: u8 = 20
const QUIC_VERSION_1: u32 = 0x00000001コンパイル時定数を定義します。型はプリミティブ整数型でなければなりません。整数リテラルを書ける場所ならどこでも使えます。
enum
enum ContentType: u8 {
ChangeCipherSpec = 20,
Alert = 21,
Handshake = 22,
ApplicationData = 23,
}整数値の名前付きセットを定義します。基礎ワイヤ型を : の後に指定します。enum 型は frame や capsule のマッチタグとしても使えます。C では typedef enum に変換されます。
flags
flags PacketFlags: u8 {
KeyPhase = 0x04,
SpinBit = 0x20,
FixedBit = 0x40,
}enum と同様ですが、値はビット演算で組み合わせて使うビットマスクです。C では typedef enum になります。enum との違いは意味的な区別のみです。
type
2 つの形式があります。
型エイリアス -- 新しいワイヤレイアウトは導入しません。
type AttHandle = u16le
type Uuid16 = u16le計算型 -- フィールドの型が先行フィールドの値に依存する依存レコード。
@strict
type VarInt = {
prefix: bits[2],
value: match prefix {
0b00 => bits[6],
0b01 => bits[14],
0b10 => bits[30],
0b11 => bits[62],
},
}継続ビット VarInt -- バイトごとの継続フラグで長さが決まる可変長整数。
type MqttLength = varint {
continuation_bit: msb,
value_bits: 7,
max_bytes: 4,
byte_order: little,
}packet
名前付きフィールドの固定シーケンスで、プロトコルヘッダを記述する基本的な構成要素です。
packet UdpDatagram {
src_port: u16,
dst_port: u16,
length: u16,
checksum: u16,
require length >= 8,
data: bytes[length: length - 8],
}frame
タグ付きユニオンです。まずタグフィールドを読み取り、パターンマッチでペイロードのレイアウトを決定します。
frame AttPdu = match opcode: u8 {
0x02 => ExchangeMtuReq { client_rx_mtu: u16le },
0x03 => ExchangeMtuRsp { server_rx_mtu: u16le },
0x0a => ReadReq { handle: AttHandle },
0x0b => ReadRsp { value: bytes[remaining] },
_ => Unknown {},
}パターンの形式:
| パターン | マッチ |
|---|---|
0x06 | 正確な値 |
0x02..=0x03 | 閉区間 [0x02, 0x03] |
_ | 任意の値(キャッチオール、網羅性のために必須) |
capsule
TLV(Type-Length-Value)コンテナです。ヘッダ部の後に within EXPR 節があり、ペイロードのパースを EXPR バイトに制約します。
capsule MqttPacket {
type_and_flags: u8,
remaining_length: MqttLength,
payload: match (type_and_flags >> 4) within remaining_length {
1 => Connect { ... },
3 => Publish { ... },
_ => Unknown { data: bytes[remaining] },
},
}タグ式(within の前)は以下のいずれかです。
- フィールド名をそのまま使う:
match content_type within length - ヘッダフィールドからの括弧付き式:
match (type_and_flags >> 4) within remaining_length
ペイロードブランチが EXPR より少ないバイトしか消費しなかった場合は WIRESPEC_ERR_TRAILING_DATA、超過した場合は WIRESPEC_ERR_SHORT_BUFFER を返します。
static_assert
static_assert MAX_CID_LENGTH <= 255コンパイル時に評価され、失敗するとコード生成が中断されます。
アノテーション
アノテーションは対象の定義の直前に記述します。
| アノテーション | 適用対象 | Phase | 効果 |
|---|---|---|---|
@endian big | モジュール(ファイル先頭) | 1 | デフォルトエンディアンネスをビッグエンディアンに設定 |
@endian little | モジュール(ファイル先頭) | 1 | デフォルトエンディアンネスをリトルエンディアンに設定 |
@strict | type 定義 | 1 | 非正規表現を WIRESPEC_ERR_NONCANONICAL で拒否 |
@checksum(internet) | フィールド(u16) | 1 | RFC 1071 Internet チェックサム:パース時に検証、シリアライズ時に自動計算 |
@checksum(crc32) | フィールド(u32) | 1 | IEEE 802.3 CRC-32:パース時に検証、シリアライズ時に自動計算 |
@checksum(crc32c) | フィールド(u32) | 1 | Castagnoli CRC-32C:パース時に検証、シリアライズ時に自動計算 |
@checksum(fletcher16) | フィールド(u16) | 1 | RFC 1146 Fletcher-16:パース時に検証、シリアライズ時に自動計算 |
@max_len(N) | 配列フィールド | 1 | フィールドごとの配列キャパシティを N 要素にオーバーライド |
@doc("...") | 任意の定義 | 1 | ドキュメント文字列(コード生成ではまだ未使用) |
@derive(...) | 定義 | 2+ | 将来の使用のために予約 |
@verify(bound=N) | 状態機械 | 3+ | TLA+ 有界検証の深さ |
@checksum は 1 つの packet / frame ブランチにつき最大 1 つです。フィールド型はアルゴリズムの要件と一致する必要があります(上表を参照)。
配列
カウントバインド配列
ack_ranges: [AckRange; ack_range_count], # 先行フィールドからのカウント
cipher_suites: [u16; cipher_suites_length / 2], # 算術カウントカウント式は整数類似型でなければなりません。実行時にフィールドのキャパシティと照合されます。
フィル配列
entries: [Entry; fill], # 現在のスコープの残りを消費[T; fill] はスコープの残りから T 要素を可能な限り読み取ります。スコープ内の最後のワイヤフィールドでなければなりません。
フィル within 配列
extensions: [Extension; fill] within extensions_length,extensions_length バイトのサブスコープを作成し、その中で Extension 要素を読み切るまでパースします。未消費のバイトが残れば WIRESPEC_ERR_TRAILING_DATA、超過すれば WIRESPEC_ERR_SHORT_BUFFER です。
配列キャパシティ(C バックエンド)
配列は固定キャパシティでスタック確保されます。デフォルトは WIRESPEC_MAX_ARRAY_ELEMENTS(64)で、wirespec_runtime.h で定義されています。-DWIRESPEC_MAX_ARRAY_ELEMENTS=N でグローバルに変更できます。
フィールド単位でのオーバーライド:
@max_len(1024)
large_items: [Item; count], # このフィールドのみキャパシティ 1024ワイヤ上のカウントがキャパシティを超えた場合、パーサーは WIRESPEC_ERR_CAPACITY を返します。
モジュールとインポート
module quic.varint # このファイルのモジュール識別子を宣言
import quic.varint.VarInt # quic/varint.wspec から VarInt をインポートモジュール名はインクルードパス(-I フラグ)上のファイルパスに対応します。コンパイラがインポートを解決し、循環を検出し、C の #include をトポロジカル順序で生成します。
規則:
- デフォルトではすべての宣言がパブリック。モジュール内のいずれかのアイテムに
exportがある場合、エクスポートされたものだけが外部から見える(リゾルバが可視性を強制する)。 - 循環インポートはコンパイルエラー。
- 相対インポートは非対応。すべて絶対モジュールパスで指定。
module宣言のないファイルも単一ファイルモードでコンパイル可能。
状態機械宣言
状態機械は state machine Name { } で定義します。完全な構文は 状態機械ガイド を参照してください。ここではキーワードと予約名の概要を示します。
キーワード
| キーワード | 役割 |
|---|---|
state machine | 状態機械定義を導入 |
state | 状態を宣言(オプションで関連データフィールドを持つ) |
initial | 初期状態を指定 |
transition | ある状態(または *)から別の状態への遷移を宣言 |
on | 遷移をトリガーするイベント |
guard | 真偽条件。true のときだけ遷移が発火する |
action | 遷移発火時に実行する代入ブロック |
delegate | イベントを子状態機械に転送 |
verify | 検証プロパティの宣言(Phase 3) |
[terminal] | 終端状態のマーク(出ていく遷移が不要) |
src および dst バインディング
遷移内では以下のバインディングが使えます。
src-- 遷移元の状態データ(guard/actionで読み取り専用)dst-- 遷移先の状態データ(actionで書き込み専用)
src / dst は guard と action ブロック内でのみ有効です。verify プロパティでは使えません(verify では裸のフィールド名で現在の状態を参照します)。
delegate セマンティクス
delegate src.field <- event はイベントを子状態機械に転送します。
dstがsrcのコピーとして暗黙的に初期化される。dst内の子フィールドがイベントを受け取り、インプレースで遷移する。- 子が別の状態に遷移した場合、
child_state_changedが親に新イベントとして発行される。 child_state_changedに対応する遷移がなければ無音で破棄される(「未処理イベント = エラー」規則の唯一の例外)。
delegate は自己遷移(遷移元と遷移先が同じ状態)でのみ使用可能です。delegate と action は同一遷移内で共存できません。
all() 量化子
guard all(src.paths[0..src.active_path_count], in_state(Closed))all(collection, predicate) は collection の全要素が predicate を満たすとき true を返します。Phase 1 でサポートされる述語は in_state(S) のみです。all() は組み込みの特殊形式であり、汎用的な高階関数ではありません。
エラーコード
C バックエンドの生成関数は wirespec_result_t を返します。取りうる値は以下のとおりです。
| コード | 意味 |
|---|---|
WIRESPEC_OK | 成功 |
WIRESPEC_ERR_SHORT_BUFFER | 入力のバイトが不足 |
WIRESPEC_ERR_INVALID_TAG | タグ値がどのパターンにもマッチせず _ もない |
WIRESPEC_ERR_CONSTRAINT | require 式が false |
WIRESPEC_ERR_OVERFLOW | 整数オーバーフロー(長さ超過、varint の max_bytes 超過など) |
WIRESPEC_ERR_INVALID_STATE | 状態機械が対応する遷移のないイベントを受信 |
WIRESPEC_ERR_TRAILING_DATA | within スコープが完全に消費されていない |
WIRESPEC_ERR_NONCANONICAL | @strict 型が非正規(非最小長)形式でエンコードされている |
WIRESPEC_ERR_CAPACITY | 配列カウントがフィールドのキャパシティを超過 |
WIRESPEC_ERR_CHECKSUM | パース時の @checksum 検証失敗 |
WIRESPEC_ERR_SCOPE_UNDERFLOW | サブカーソルのアンダーフロー(内部エラー) |
WIRESPEC_ERR_ARRAY_OVERFLOW | 配列インデックスが範囲外(内部エラー) |
文法サマリー
完全な形式文法は 文法リファレンス にあります。主要な生成規則を以下に示します。
file = { annotation | module_decl | import_decl | top_item }
top_item = const_def | enum_def | flags_def | type_def
| packet_def | frame_def | capsule_def | static_assert_def
type_expr = type_ref
| "match" NAME "{" match_branch { "," match_branch } "}"
| "if" expr "{" type_expr "}"
| "[" type_expr ";" array_size "]"
| "bytes" "[" bytes_spec "]"
bytes_spec = INTEGER
| "remaining"
| "length" ":" expr
| "length_or_remaining" ":" expr
pattern = literal | literal "..=" literal | "_"