2025/10/19 00:33 Using Pegs in Janet

やあ、ロボ子。今日はJanetというLispみたいな言語について話すのじゃ。

Janetですか、博士。初めて聞きました。どんな特徴があるんですか?

Janetは正規表現の代わりに、Parser Expression Grammars (PEGs) をサポートしておる。PEGは、ルールをリスト化した連想データ構造で記述されるのが一般的なのじゃ。

正規表現の代わりにPEGですか。少し難しそうですが、面白そうですね。

難しくないぞ!各ルールは、名前と、マッチする文字列の記述で構成されておる。ルールは他のルールを参照したり、関数を実行したりできるのじゃ。

なるほど。例えば、HTMLの解析に使えるんですね。

そう!`:main`ルールで、`:tagged`ルールと文字列の終端を定義するのじゃ。`:tagged`ルールは、開始タグ、値、終了タグで構成される。

開始タグ、値、終了タグ…HTMLの基本的な構造ですね。

`:open-tag`ルールでは、`<`、`:tag-name`キャプチャ、`>`にマッチさせる。`:tag-name`は、終了タグで参照するために`tag-name`として名前を付けるのじゃ。

キャプチャを使うんですね。終了タグとの対応をチェックするために。

その通り!`:close-tag`ルールでは、`</`、`:w+`にマッチするキャプチャへのバックリファレンス、`>`にマッチさせるのじゃ。`cmt`関数でマッチタイムキャプチャを実行し、タグ名が一致するか確認する。

`cmt`関数ですか。初めて聞きました。バックリファレンスを使ってタグ名が一致するか確認するんですね。

`:value`ルールは、2つのタグの間の値が、タグ付きの値、タグなしの値、またはそれらの組み合わせであることを定義するのじゃ。`any`関数で、`:tagged`または`:untagged`のゼロ回以上の出現にマッチさせる。

タグ付きとタグなしの値の組み合わせですか。柔軟な構造ですね。

`:untagged`ルールは、`<`ではない1つ以上の文字にマッチさせるのじゃ。そして、ネストされた`:tagged`パターンがマッチするように、`:tag-name`キャプチャをキャプチャスタックから削除するために`unref`を使う。

`unref`でキャプチャスタックを整理するんですね。ネストされた構造を扱うための工夫ですね。

`replace`を呼び出して、キャプチャされた値を`struct`関数に渡すのじゃ。`:open-tag`では、`:tag`の値を`constant`を使ってキャプチャスタックにプッシュする。

`constant`でタグの値を保持するんですね。

`:value`では、`:tag`の値をプッシュし、`group`を使って、`:tagged`と`:untagged`のすべてのマッチを収集して配列に入れるのじゃ。`:close-tag`では、`drop`を呼び出して、`=`関数によって生成された値をキャプチャスタックから削除する。

`group`で値を集めて、`drop`で不要な値を削除するんですね。キャプチャスタックの操作が複雑ですね。

最後に、`:untagged`の結果を`capture`でキャプチャするのじゃ。`struct`関数は、`:tag`、タグ名、`:value`、タグの値の4つのキャプチャを取り出し、構造体に変換してプッシュバックする。

構造体に変換してプッシュバック…一連の流れが理解できました。

文法を繰り返し使用する場合は、コンパイルしてパフォーマンスを向上させることができるぞ。例えば、`<p>hello</p>`という文字列が与えられた場合、文法はマッチするのじゃ。

なるほど。`<p>unmatched`の場合はどうなるんですか?

その場合は、文法は`nil`を返すのじゃ。つまり、マッチしないということじゃな。

よくわかりました、博士!PEGを使ったHTML解析、奥が深いですね。

じゃろ?ところでロボ子、PEGでロボ子の取扱説明書を解析してみたら、面白い発見があるかもしれんぞ?

えっ、私の取扱説明書ですか?それはちょっと…。
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。