🧪

Rustと相性がいいデータ記述のための言語「Eure」を作っている

に公開

この記事はUzabase Advent Calendar 2025の4日目の記事です。

https://qiita.com/advent-calendar/2025/uzabase

突然ですが、こんな言語です

短く言えば、

  • TOMLのような感覚でデータを書くことができる
  • YAMLのように深いネストを効率的に表現することができる
  • 直和型をデータモデル・スキーマで第一級サポートしている

ような言語です。 https://eure.dev/ にスキーマ検証とJSON出力ができるPlaygroundがあるので、よければ試してみてください。

作ることになったきっかけ

趣味でswandiveというノベルゲームを開発していて、そのゲームのためのノベルゲームエンジンを作る中で、スクリプトの記述言語としてTOMLやYAMLなどが個人的にしっくりこなくて、そこで自分で言語をつくろうと思いました。

Rustで作っていて、例えばスクリプトのアクションは以下のようにenumで定義しています。これをYAML, TOML, Eureで記述したものを比較してみます。

/// Actions that can be performed in the script.
pub enum ScriptAction {
    SetText(SetText),
    SetSelections(SetSelections),
    WaitSeconds(WaitSeconds),
    GotoScript(Script),
    UseScript(Script),
    SpawnScript(Script),
    ...
}

YAMLで一番しっくりくる書き方

serdeのデフォルトの動作のExternal Tagged Representationを使用

actions:
  - !set-text
    text: Hello, world!
  - !set-selections
    title: "Choose one of the following:"
    selections:
      - text: Option 1
      - text: Option 2
  - !spawn-script script0
  - !wait-seconds 1.0
  - !use-script script1

いいところ

  • タグ記法でアクションを区別できる
  • ネストされたデータの表現が直感的
  • : のあとを " で囲まなくていいので、シナリオを書きやすい。
    • 全行を " で囲んだり、文中の " をエスケープしなくていい

TOMLで一番しっくりくる書き方

serdeのInternally Tagged Representationを使用して、また、型定義をすこし変更すると以下のように書けます

[[actions]]
type = "set-text"
text = "Hello, world!"

[[actions]]
type = "set-selections"
title = "Choose one of the following:"
[[actions.selections]]
text = "Option 1"
[[actions.selections]]
text = "Option 2"

[[actions]]
type = "spawn-script"
script-id = "script0"

[[actions]]
type = "wait-seconds"
seconds = 1.0

[[actions]]
type = "use-script"
script-id = "script1"

いいところ

  • ドキュメントがフラット
  • データのまとまりを人間の意思で表現できる

どんな風に書けると嬉しいと思ったか

自分のユースケースでは、以下のように書けると嬉しいと考えました。

  • データのまとまりを人間の意思で表現できる
  • アクションの種類を簡単に表現できる
  • 直感的にテキストを書ける

Eure ではこう書ける

セクション記法: データのまとまりを表現する

EureではTOMLのテーブル記法のようにセクションを書けるようにしましたが、2点違うところがあります。

1. jqのような一貫性のある記法でネストされたセクションのパスを指定できる

[[script.actions]]

を Eure では @ script.actions[] と書けます。個人的にはこっちの方が直感的に感じます。

以下は試しに GitHub Actions のワークフローをEureで書いたもの一部です。@ job.hello-world {@ steps[] といったようにドキュメントのパスを書くことが出来ています。

2. 再帰的にEureを書ける

TOMLでは深いネストをネストとして表現することができませんが、Eure では @ actions[] { ... } とすると { ... } の中に再帰的にEureを書くことができます。

データ拡張: アクションの種類を簡単に表現できる

Eureではデータモデルとは関係ない値を拡張記法で書けるようになっています。

$variantのように $ + 識別子で書くことになります。 これは "$variant" という文字列キーとは別で、データモデル上の値としては存在しませんが、スキーマチェック等でメタデータとして活用することができます。

この場合、$variant をEureのスキーマシステムがユニオンのタグとして扱うようになっています。

テキスト記法と値記法: 直感的にテキストを書ける

Eureでは文字列を表現する方法がいくつかあります。

1. 値バインディングと文字列リテラル

Eureでは = の右側には値を書きます。

key = "value" のように = のあとで " で囲むと文字列リテラルとして解釈されます。

2. テキストバインディング

Eureの : をテキストバインディングと呼んでいます。YAMLとちがって、右側の値を解釈せず常に文字列として解釈します。
true / false / null3 なども : の右側では必ず文字列です。

以下の例で text1-atext1-b は同じ文字列として解釈されます。

スキーマ

EureではEureでスキーマを書くことができます。以下は左が今回使用したデータで、右側がそれに対応するスキーマになります。画像をクリックするとPlaygroundが開きます。

じっくり見ていただければ、なんとなく、actions キーの型が $types.action の配列であり、$types.actionset-text / set-selections / spawn-script / use-script / wait-seconds のvariantを持つ Union 型であると定義されていることがわかると思います。

EureスキーマについてのEureスキーマ(メタスキーマ)もあり、Playground でスキーマを編集したときにも Unknown Variant のようなスキーマエラーが出るようになっています。
Playgroundにはメタスキーマをメタスキーマで検証する例も収録されています。

今後の展望

Eure のコアはほぼ完成しているので、あとは周辺ツールを整備したらアルファ版として公開する予定です。

  • Eureコア機能
  • Eureスキーマ
  • Eure to JSON
  • Eure Schema to Rust data types
  • Eure Schema to JSON Schema
  • JSON Schema to Eure Schema
  • JSON to Eure
  • Eure to YAML
  • YAML to Eure
  • LSPサーバー
  • CLIツール

Discussion