Slide 1

Slide 1 text

Wasmで拡張できる 軽量マークアップ言語 Brack (前編) 第14期サイボウズ・ラボユース成果発表会 2025.03.28 浅田睦葉 @mutsuha_asada GitHub

Slide 2

Slide 2 text

2 @momeemt @mutsuha_asada https://momee.mt 浅田 睦葉(Mutsuha Asada) 🎓 所属 ・筑波大学情報学群情報科学類 B3 🐣 興味があること ・コンパイラやそのツールチェーン(LSP、サニタイザ) ・ビルドシステム(Nix, Meson, etc...) 🍳 趣味 ・音楽、お笑い、料理など 自己紹介

Slide 3

Slide 3 text

概要 3 ・開発中のマークアップ言語についてお話しします 📚 目次 マークアップ言語を自作する背景 1. 言語仕様とプラグインシステム 2. ブログジェネレータの実装 3. ビルドツールの実装 4. 展望とまとめ(前半) 5. Language Serverの導入 6. パーサの概要 7. エラートレラントなパーサの実装 8. 展望とまとめ(後半) 9.

Slide 4

Slide 4 text

1. マークアップ言語を自作する背景 4

Slide 5

Slide 5 text

軽量マークアップ言語を自作する背景 5 ・ブログを書くため  ・ 🤔「自作言語でブログ書きたくね?」  ・最初は雑な思い付き ・たいていの場合はMarkdownが採用される  → Zenn、Qiita、GitHub README、その他、etc...   👍 データ記述の整合性を保ちつつ、ソースコードの可読性も意識されている   😞 ブログの場合には変換後のコンテンツのみが重要 + 多様な表現力が求められる   😞 標準的で明文化された仕様を持たないので方言が無数に存在 1. マークアップを自作する背景

Slide 6

Slide 6 text

Markdownクイズ 😄 6 ・Markdownはオフィシャルな統一された規格が無く、  CommonMarkやGitHub Flavored Markdown(GFM)などの後発の規格がいくつかある  → 脚注はCommonMarkに定義されている?GFMには?  → インライン脚注はpandocで変換できる?  → リストの要素に見出しを含めてMarkdown.plで変換できる?CommonMark準拠だと?  → MultiMarkdownを使うとルビを振ることはできる? ・我々はMarkdownの枠を超えた表現力をMarkdownに要求している まあ確かに欲しいよな 知人のMarp製スライド pタグを省略できる JSXであるところのMDX 1. マークアップを自作する背景

Slide 7

Slide 7 text

1. マークアップを自作する背景 Markdownクイズ 😄 7 ・Markdownはオフィシャルな統一された規格が無く、  CommonMarkやGitHub Flavored Markdown(GFM)などの後発の規格がいくつかある  → 脚注はCommonMarkに定義されている?GFMには?  → インライン脚注はpandocで変換できる?  → リストの要素に見出しを含めてMarkdown.plで変換できる?CommonMark準拠だと?  → MultiMarkdownを使うとルビを振ることはできる? ・我々はMarkdownの枠を超えた表現力をMarkdownに要求している まあ確かに欲しいよな 知人のMarp製スライド pタグを省略できる JSXであるところのMDX 変換規則・対象を拡張することを 前提にしたマークアップ言語が欲しい

Slide 8

Slide 8 text

作りたい言語のイメージ 8 ・言語仕様に特定の装飾が含まれない ・コマンドはモジュール名 + コマンド名 + 引数からなる文字列置換メカニズム ・コマンドは外部プログラムの呼び出しによって実現する 1. マークアップを自作する背景

Slide 9

Slide 9 text

プロトタイプ (2022) 9 ・Nim言語で、Nimの関数を呼び出すBrack言語とブログジェネレータを実装  → 詳しくはこの記事を読んでください 1. マークアップを自作する背景

Slide 10

Slide 10 text

プロトタイプ (2022) の評価 10 ・ 👍 良い点  ① 毎日の日報を書くためのブログ言語として十分に動作する   → https://blog.momee.mt はこのプロトタイプで動いている  ② コマンドとマクロが実装されており、Nimの関数で直感的に書ける ・ 😞 改善するべき点  ① 独自のコマンドを実装するためにはNimを書かなければならない  ② プラグインが静的に解決されるため、追加・削除するにはビルドし直すしかない  ③ ブログがNimが提供する謎のテンプレートエンジンによって生成されており、   現代的なフロントエンド技術と組み合わせることが難しい   → 真面目に動かすには車輪の再発明をたくさんしなければいけない 1. マークアップを自作する背景

Slide 11

Slide 11 text

2. 言語仕様とプラグインシステム 11

Slide 12

Slide 12 text

12 プラグインシステムの変更 ・2023年12月ごろに友人(上野)がBrackの開発に関心を示した  → まずは問題点①、②を解決してからチーム開発を始めることにした ・動的にプラグインをロード・実行できる仕組みを入れる  ・選択肢   ・動的リンク(so, dylib, dll)   ・Luaなどのスクリプト言語のランタイムを組み込む   ・VM言語で実装し直して、中間形式をプラグインとして扱う(JVM、.NETなど)   ・WebAssembly  ・Wasmは移植性が高く、サンドボックスモデルを持つ   → 将来的なエコシステムの拡充も見込める   ・これを入れよう! 2. 言語仕様とプラグインシステム

Slide 13

Slide 13 text

13 Nimのことはもう忘れても大丈夫です 🥲 ・Wasmをプラグインとして扱うためのフレームワーク、Extism  ・少ないコードでホスト + プラグイン側の実装が可能  ・Rustで実装されたライブラリだった   → ので、コンパイラもRustで実装し直すことに ▲ 2日で実装が急にRustに書き換わる 2. 言語仕様と プラグインシステム

Slide 14

Slide 14 text

Extism Wasmtime (Wasm Runtime) 実 行 Brack Compiler 解析・AST化 必要な関数をコール 呼び出し・ABIの解決 (実行時) 14 Wasmを用いたプラグインシステム Brack文書 プラグイン 様々な言語から コンパイル 2. 言語仕様とプラグインシステム

Slide 15

Slide 15 text

15 コマンドとマクロ ・コマンドは単なる文字列の置換メカニズム  → 脚注のような複数の要素にまたがるコンテンツを実装できない ・マクロ  ・マクロはコード生成前に、ASTを渡してのぞみの構造に変換して返す   → より自由なコンテンツを生成することができる 2. 言語仕様とプラグインシステム

Slide 16

Slide 16 text

3. ブログジェネレータの実装 16

Slide 17

Slide 17 text

17 旧来のブログジェネレータ ・プロトタイプでは生成したHTMLを  Nimのテンプレートエンジンに埋め込んだ  → GitHub ActionsでHTML/CSS/JSを   生成して配信サーバでホスト 👍 コンパイラが吐くHTMLをそのまま使える 😞 モダンなフロントエンド技術との  相性が悪い 3. ブログジェネレータの実装

Slide 18

Slide 18 text

Brack Compiler HTMLを生成 Blog Generator JSONに変換 18 実装したブログジェネレータ ・モダンなフロントエンドと組み合わせるためにJSONを出力  → Next.jsプロジェクトをテンプレートにしてブログ記事を描画 記事データ fetch フロントエンド 👍 フロントエンドとBrackを分離できているので フロントエンド技術自体はなんでも良い 3. ブログジェネレータの実装

Slide 19

Slide 19 text

19 生成されるブログ ・素のBrack文書から右のようなブログ記事を出力できる  → フロントエンドは既存の資産に乗っかった上で   開発できるので旧ブログのような辛さが全くない ・単に記事データのJSONをパースして描画するだけなので  自分でデザインを自由に決めることができる  ・謎のテンプレートや規約に乗っかる必要がない  → WordPressなどのCMSと比較した嬉しさ

Slide 20

Slide 20 text

20 容易なデプロイ ・公式で定義したActionを  呼び出すだけでデプロイ完了 ・以前より簡単に技術ブログを  始めることができる 3. ブログジェネレータの実装

Slide 21

Slide 21 text

4. ビルドツールの実装 21

Slide 22

Slide 22 text

22 ビルドツールの必要性 ・以前はNimプログラムをビルドする際にプラグインも解決されていた  → しかし、新しいコンパイラは実行時にWasmバイナリを読みに行く  ・プラグインのパス、依存関係や文書の設定を記述できるビルドツールが必要 ・どんな要求を満たしたい?  ・1バイナリであること   → 軽量マークアップ言語を使いたい人が文書を書くことに興味があるのであって    残念ながら複雑なビルドやインストール工程には興味がない  ・グローバルを汚染しないこと   ・Wasmバイナリは1つ1つが軽量なのでプロジェクトごとに用意しても問題ない  ・エラー報告がわかりやすい  ・設定ファイルはtomlで書きたい 4. ビルドツールの実装

Slide 23

Slide 23 text

23 4. ビルドツールの実装 ビルドツールの必要性 ・以前はNimプログラムをビルドする際にプラグインも解決されていた  → しかし、新しいコンパイラは実行時にWasmバイナリを読みに行く  ・プラグインのパス、依存関係や文書の設定を記述できるビルドツールが必要 ・どんな要求を満たしたい?  ・1バイナリであること   → 軽量マークアップ言語を使いたい人が文書を書くことに興味があるのであって    残念ながら複雑なビルドやインストール工程には興味がない  ・グローバルを汚染しないこと   ・Wasmバイナリは1つ1つが軽量なのでプロジェクトごとに用意しても問題ない  ・エラー報告がわかりやすい ベースは`cargo`コマンド CLIコマンドは`go`コマンド を目指した作りにしたい

Slide 24

Slide 24 text

24 CLIとLanguage Server、デバッグの抽象化 ・CLIとLanguage Server、デバッグはそれぞれ「データの文字列加工屋さん」  ・ロガーをそれぞれ独自に実装して、プロジェクト管理クレートに渡してあげると   上手く抽象化できる brack-cli brack-project brack-language-server debug brack-tokenizer brack-parser brack-codegen ここで必要な情報を集めて整理する 👉 4. ビルドツールの実装

Slide 25

Slide 25 text

25 Cargo.toml風の設定ファイル 4. ビルドツールの実装

Slide 26

Slide 26 text

26 動作の様子 4. ビルドツールの実装

Slide 27

Slide 27 text

5. 展望とまとめ(前半) 27

Slide 28

Slide 28 text

28 展望(前半) ・CLIが充実してきたらv0.2.0を打ちたい、3〜4月中を目標にする ・今まではエラー報告がわかりにくくインストール工程も複雑だったのであまり  積極的に宣伝してこなかったが、そのタイミングでdev.toやHackerNewsなどに  投稿してフィードバックを得たいと考えている ・今のコンパイラを自分のブログ(blog.momee.mt)で実用できるようにしたい ・Brackのプラグインを実装するためのライブラリはExtismを  薄くラップしたもので、Nimで実装していたころより遥かに複雑なので  改善していきたい ・ブログのデプロイをさらに簡易化して、友人にブログジェネレータを使って  ブログを書いてもらいたい  → 現在はジェネレータとコンパイラが分かれているので統合する 5. 展望とまとめ(前半)

Slide 29

Slide 29 text

29 まとめ(前半) ・Wasmで変換規則を拡張できるBrackという軽量マークアップ言語を作っている ・ブログを書くための言語として作っていて、将来的にはスライドやPDFも吐きたい ・元々はNimで実装していて、定義したコマンドをコンパイル時に結合して実現して いたが、① ライブラリの実装者がNimを書かなければいけない ② ライブラリが増減 するときにビルドし直さなければいけない ③ Nimのテンプレートエンジンに密結合し ておりモダンなフロントエンド技術と組み合わせるのが困難という問題があった ・そこでWebAssemblyをプラグインとして利用するコンパイラをRustで書き直した ・ブログジェネレータが生成したHTMLをJSONに変換するようにしたので、モダンな フロントエンド技術と組み合わせることが容易になった ・ブログを書くためのCLIやデプロイワークフローについても実装した ・ビルドツールの実装をしていて、使い勝手はCargoに近く、1バイナリで完結する点 やコマンドのインタフェースはGoに近いものを目指している 5. 展望とまとめ(前半)

Slide 30

Slide 30 text

Wasmで拡張できる 軽量マークアップ言語 Brack (後編) 第14期サイボウズ・ラボユース成果発表会 2025.03.28 上野幹太 @uekann_ GitHub

Slide 31

Slide 31 text

自己紹介 31 @uekann @uekann_ https://uekann.com (準備中) 上野 幹太(Kanta Ueno) 🎓 所属 ・筑波大学情報学群情報科学類 B3 🐣 興味があること ・ブラックボックス最適化(CMA-ESなど) ・なんだろう...... 🍳 趣味 ・写真、スキー、散歩、音楽など

Slide 32

Slide 32 text

概要 32 ・開発中のマークアップ言語についてお話しします 📚 目次 マークアップ言語を自作する背景 1. ブログジェネレータの実装 2. ビルドツールの実装 3. プラグインシステムの実装 4. 展望とまとめ(前半) 5. Language Serverの導入 6. パーサの概要 7. エラートレラントなパーサの実装 8. 展望とまとめ(後半) 9.

Slide 33

Slide 33 text

6. Language Serverの導入 33

Slide 34

Slide 34 text

Language Serverとは 34 コード補完や診断、リファクタリング支援を行うバックエンド rust-analyzerなどが有名 入力補完 エラー表示 6. Language Serverの導入

Slide 35

Slide 35 text

https://code.visualstudio.com/api/language-extensions/language-server-extension-guide LSP (Language Server Protocol) 35 Microsoftが規定したLanguage Serverの通信仕様 (Language ServerそのものをLSPと呼ぶ風潮もありそう) 6. Language Serverの導入

Slide 36

Slide 36 text

LSP (Language Server Protocol) 36 Microsoftが規定したLanguage Serverの通信仕様 VSCodeでのエラー表示 Neovimでのエラー表示 6. Language Serverの導入

Slide 37

Slide 37 text

LSP (Language Server Protocol) 37 Microsoftが規定したLanguage Serverの通信仕様 → BrackもLanguage Serverを作ろう! Brackに関するドキュメントが充実していなくても、 Language Serverから得られる情報によって、 執筆体験が損なわれないようにしたい 6. Language Serverの導入

Slide 38

Slide 38 text

Language Serverの実装 38 作りました! 🥳 コンパイルエラー 存在しないプラグイン、コマンド 入力補完 etc... 6. Language Serverの導入

Slide 39

Slide 39 text

Language Serverの実装 39 困りごと これはLanguage Serverの問題ではなく、コンパイラそのものが抱える問題 今回はLanguage Serverを実装したタイミングで表面化した 構文エラーが1つしか表示されない → 構文エラーが複数ある場合、1つ直すと別のエラーが出てくる。(つらい) 複数の構文エラーを同時にキャッチできるパーサを作ろう! 6. Language Serverの導入

Slide 40

Slide 40 text

7. パーサの概要 40

Slide 41

Slide 41 text

抽象構文木とは 41 構文としての意味を持つ木構造 言語の意味に関係ない情報を取 り除いた構文木 document stmt curly module command arg stmt expr square 7. パーサの概要

Slide 42

Slide 42 text

再帰下降パーサ 42 document stmt curly { std . * Hello } 7. パーサの概要

Slide 43

Slide 43 text

再帰下降パーサ 43 document stmt curly module { std . * Hello } 7. パーサの概要

Slide 44

Slide 44 text

再帰下降パーサ 44 document stmt curly module command { std . * Hello } 7. パーサの概要

Slide 45

Slide 45 text

再帰下降パーサ 45 document stmt curly module command arg { std . * Hello } 7. パーサの概要

Slide 46

Slide 46 text

document stmt curly module command arg エラートレラントなパース 46 パースに失敗しても読み飛ばしてエラーを回収したい 7. パーサの概要

Slide 47

Slide 47 text

document stmt curly module command arg エラートレラントなパース 47 パースに失敗しても読み飛ばしてエラーを回収したい ・どこまでASTを遡って ・どこまでトークンを読み飛ばして パースを再開する? ? 無限に条件分岐が必要... 7. パーサの概要

Slide 48

Slide 48 text

8. エラートレラントなパーサの実装 48

Slide 49

Slide 49 text

解決策 49 ASTを構成する際に、すでに木構造があれば解決! document stmt curly module command arg stmt expr square 8. エラートレラントなパーサの実装

Slide 50

Slide 50 text

具象構文木(CST)の導入 50 document stmt curly module command arg stmt expr square document ? stmt? curly? module? command? arg? stmt? expr? square? ゆるい規則に基づいて木構造(CST)を構成 ASTへの変換を試みる 8. エラートレラントなパーサの実装

Slide 51

Slide 51 text

CSTの規則 51 ・任意のトークン列から構成可能 ・正しい構文のドキュメントは正しく解釈する ・誤った構文のドキュメントも“可能な限りそれっぽい”木構造に落とす 8. エラートレラントなパーサの実装

Slide 52

Slide 52 text

具象構文木(CST)の導入 52 ASTのBNF(一部) CSTのBNF(一部) 規則、作りました お気持ち 上位のノードはできる限り 正しくあってほしいので、 厳しめの規則 8. エラートレラントなパーサの実装

Slide 53

Slide 53 text

Language Serverでの表示 53 8. エラートレラントなパーサの実装

Slide 54

Slide 54 text

9. 展望とまとめ(後半) 54

Slide 55

Slide 55 text

55 展望(後半) ・Language Serverの機能拡充 Hover、Rename、補完... ・型チェック(?)の導入 tableコマンドなどの引数に特定のコマンドを指定したい場合などがある →コンパイラでチェックしたい 現在ASTから実行までの間に中間表現を導入することを検討し、 実装を始めているが、メリットが怪しくなってきました ・デバッグ環境、テスト環境の整備 現在毎回printデバッグをしていてつらい テストも抜けていたりする 9. 展望とまとめ(後半)

Slide 56

Slide 56 text

56 まとめ(後半) ・Language Serverを実装 パースエラーの表示、簡単な入力補完、引数の情報などを渡せるようになった 引数の個数が間違っている等の、パースエラー以外のコンパイルエラーを複数表示し たり、フォーマットやより賢い入力補完などは今後実装していきたい ・複数のパースエラーの検知 再帰下降パーサでは、パースに失敗した時どこまで処理を遡って再開すれば良いかわ からないので、ASTを構成することで一度別の木構造に落としておくことで、ASTへ の変換に失敗した場合でも次にどこを見ればいいかわかるようにした 9. 展望とまとめ(後半)