Skip to content

モジュールとインポート

wirespec はモジュールシステムで複数ファイル構成をサポートします。各 .wspecファイルは名前付きモジュールに属し、型をモジュール間でインポートできます。

モジュール宣言

.wspec ファイルの先頭で module を宣言します:

wire
module quic.varint

@strict
type VarInt = {
    prefix: bits[2],
    value: match prefix {
        0b00 => bits[6],
        0b01 => bits[14],
        0b10 => bits[30],
        0b11 => bits[62],
    },
}

ドット区切りのモジュール名はファイルパスに直接対応します。quic.varintquic/varint.wspec(または quic/varint.wspec)。ドットがディレクトリセパレータです。

モジュール名ファイルパス
quic.varintquic/varint.wspec
quic.framesquic/frames.wspec
mpquic.pathmpquic/path.wspec
net.udpnet/udp.wspec

インポート

import で別モジュールの型をスコープに取り込みます:

wire
module quic.frames
@endian big

import quic.varint.VarInt

const MAX_CID_LENGTH: u8 = 20

packet AckRange {
    gap: VarInt,
    ack_range: VarInt,
}

frame QuicFrame = match frame_type: VarInt {
    0x00 => Padding {},
    0x01 => Ping {},
    0x06 => Crypto {
        offset: VarInt,
        length: VarInt,
        data: bytes[length],
    },
    # ...
    _ => Unknown { data: bytes[remaining] },
}

インポートパス quic.varint.VarInt の形式は module.name.TypeName。最後がインポートする型名、それ以前がモジュール名です。

複数のインポートは 1 行ずつ:

wire
module myapp.protocol

import quic.varint.VarInt
import ble.att.AttHandle
import net.udp.UdpDatagram

ファイルパスの解決

import quic.varint.VarInt を見つけると、コンパイラはモジュール名 quic.varint を以下の手順でファイルに解決します:

  1. ドットをディレクトリセパレータに変換: quic.varintquic/varint
  2. .wspecを付加: quic/varint.wspec
  3. インクルードパスを順に検索

デフォルトのインクルードパスはカレントディレクトリです。-I path/ で検索ルートを追加:

bash
# Resolves quic/varint.wspec relative to examples/
wirespec compile examples/quic/frames.wspec -I examples/ -o build/

どのインクルードパスにもファイルが見つからなければエラーになります:

error: module 'quic.varint' not found
  searched: ./, examples/

複数モジュールプロジェクトのコンパイル

単一モジュール(インポートなし)

bash
wirespec compile examples/net/udp.wspec -o build/

インポートありのモジュール

ルートモジュールを指定し、依存モジュールを含むインクルードパスを追加します:

bash
wirespec compile examples/quic/frames.wspec -I examples/ -o build/

コンパイラが全インポートを解決し、依存モジュールをコンパイルして出力を生成します。

再帰モード

--recursive で指定ファイルと全推移的依存をまとめてコンパイルします:

bash
wirespec compile examples/quic/frames.wspec -I examples/ --recursive -o build/

依存が深い大規模プロジェクトで便利です。

コード生成なしのチェック

bash
wirespec check examples/quic/frames.wspec

出力ファイルを書かずにファイルをパース・型チェックします。CI で .wspec ファイルだけ検証したいときに便利です。check コマンドは入力ファイルのみを受け取り、-I フラグには対応していません。

生成される出力

各モジュールから .h / .c のペアが生成されます。モジュール名がファイルプレフィックスとなり、ドットはアンダースコアに置換:

build/
  quic_varint.h      # quic.varint → quic_varint prefix
  quic_varint.c
  quic_frames.h      # quic.frames → quic_frames prefix
  quic_frames.c

quic.framesquic.varint をインポートしている場合、生成される quic_frames.h は依存を自動インクルードします:

c
/* Auto-generated by wirespec compiler -- DO NOT EDIT */
#ifndef WIRESPEC_QUIC_FRAMES_H
#define WIRESPEC_QUIC_FRAMES_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#include "wirespec_runtime.h"
#include "quic_varint.h"    /* imported: quic.varint.VarInt */

/* ... struct and function declarations ... */

#endif /* WIRESPEC_QUIC_FRAMES_H */

#include の手動管理は不要です。コンパイラが依存を追跡して正しいインクルードを生成します。

エクスポートと可視性

デフォルトではモジュール内の全型がインポート可能です。内部型を隠したい場合は、公開する定義に export を付けます:

wire
module mylib.codec

# Only PublicHeader is importable from outside this module
export packet PublicHeader {
    version: u8,
    length: u16,
}

# InternalHelper is not visible to importers
packet InternalHelper {
    checksum: u32,
}

規則:

  • モジュール内のいずれかのアイテムに export がある → エクスポートされたものだけが外部から見える
  • どのアイテムにも export がない → 全て公開(後方互換)

既存モジュールに段階的に可視性を導入できます。公開したいアイテムに export を付ければ、残りは自動的に非公開になります。

循環参照の検出

循環インポートはコンパイル時に検出されエラーになります:

error: circular import detected: a → b → a

循環依存はサポートされません。共有型を共通の基底モジュールに切り出してください:

wire
# shared/types.wspec
module shared.types
export type Handle = u16le

# module_a.wspec
module module_a
import shared.types.Handle

# module_b.wspec
module module_b
import shared.types.Handle

例: QUIC VarInt + フレーム

QUIC の例は 2 モジュール構成です。quic/varint.wspec で可変長整数を定義し、quic/frames.wspec がインポートします:

bash
# Compile frames.wspec; the compiler finds varint.wspec via -I examples/
wirespec compile examples/quic/frames.wspec -I examples/ -o build/

# Build the generated C alongside your tests
cd build
gcc -Wall -Wextra -Werror -O2 -std=c11 -I../runtime \
    -o test_frames quic_varint.c quic_frames.c test_quic_frames.c
./test_frames

依存順序は自動的に処理されます。quic_frames.cquic_varint.c に依存するので、正しい順序で出力されます。