octahedron

LemとSKKとCommon Lispでたたかうプログラマのブログ

Rosa --- メタデータ付きテキストを表現する言語

Rosa

 Rosaは、プレーンテキストにタイトルや作成者などのメタデータを付与するための、メタなマークアップ言語です。また、そのパーサライブラリであり、パーサコマンドの名前でもあります。

 ちなみにCommon Lispで実装しました!!!

github.com

動機

  • ムサシ「何だかんだと聞かれたら」
  • コジロウ「聞かせてあげよう我らが名を」

ロケット団 (アニメポケットモンスター), 口上, 5代目(SM編)- Wikipedia

 不躾ながらも文章やらなんやらを書くのが趣味なのです。そして書いた文章は、題名や書いた日付や、その他脚注やメモや参考にしたURLなんぞといっしょに同じテキストファイルに残しているわけなんです。そういう付加的な情報はファイルの中に書いておきたい*1

 でも、そんな記法・言語ってあったっけ?

 マークアップ言語の中で候補を考えると、XMLMarkdown、reStructuredText、JSONYAMLなどなどいろいろありますが、以下の理由でそれぞれ却下です:

  • XML … あの構文はプレーンテキストと相性が悪い
  • Markdown … 構文は軽いが、名前を任意に埋め込むのは範疇ではない
  • reStructuredText … 構文は軽いが、マークアップ部分がオーバースペック
  • JSON … 名前とデータを記述するのにはいいが、プレーンテキストにブラケットはちょっと…
  • YAMLJSONよりはいいけど、プレーンテキストにインデントはちょっと…

 そもそも、前述の言語は文書構造を表現するものたちであって、付加的な情報を表現するものではないのです。探した限りで、そういう言語は見当たりませんでした。

──そんな言語がないのなら、つくるしかないじゃない! あなたも! わたしも…!!

<2017-03-24追記>

 このrosaを、つくっておきながらジャンルがよくわからなかったとき、Masaiさんからメタマークアップ言語では?と教えていただきました。ありがとうございます!

で、どんな言語?

 というわけでつくったのがrosaでございます。

 どんな言語か紹介します。まず、見た目でいうとこんな感じ。

:title あのときの王子くん
:author アントワーヌ・ド・サン=テグジュペリ
:source-site あおぞら文庫
:source-url http://www.aozora.gr.jp/cards/001265/files/46817_24670.html

:body

〈星から出るのに、その子はわたり鳥をつかったんだとおもう。〉

[#改ページ]


レオン・ウェルトに

 子どものみなさん、ゆるしてください。ぼくはこの本をひとりのおとなのひとにささげます。でもちゃんとしたわけがあるのです。そのおとなのひとは、ぼくのせかいでいちばんの友だちなんです。それにそのひとはなんでもわかるひとで、子どもの本もわかります。しかも、そのひとはいまフランスにいて、さむいなか、おなかをへらしてくるしんでいます。心のささえがいるのです。まだいいわけがほしいのなら、このひともまえは子どもだったので、ぼくはその子どもにこの本をささげることにします。おとなはだれでも、もとは子どもですよね。(みんな、そのことをわすれますけど。)じゃあ、ささげるひとをこう書きなおしましょう。

...

 Rosaのファイルはテキストデータメタデータから成ります。テキストデータに付けた名前(メタデータ)を、:から始まる行で表現します。メタデータの名前のことをrosaのREADMEの中ではラベルラベルと呼称しているので、以降もラベルということにします。

 一行だけのテキストデータと、改行を含むテキストデータで書き方がすこし違います。

インラインラベル

 一行のほうは、:の次から最初のスペースまでがラベルです(インラインといいます)。例を示すと、

:hoge-inline Common Lispがすきです

こうです。二つ目以降のスペースはテキストデータの一部になります(ちなみにラベルは[a-z][a-z-]+正規表現じゃなければ、ふつうの行だと認識されます)。

ブロックラベル

 複数行のほうは、:からスペースなしの改行までがラベルになります(ブロックといいます)。対応するテキストデータの範囲は、次の行から、その次のラベルかファイル終端までです。例示すると、

:hoge-block

Commmon Lispが最高に好きです。
Emacsも好きです。

:fuga-inline モクローも好きです。

こう。

コメント&エスケープシーケンス

 あと、ブロックラベルには、複数行コメント(行頭の;はじまり)があったり、:;に対するエスケープシーケンスがあったりします:

:body

; この行は無視されます
:; この行はセミコロンから始まります
:: この行はコロンから始まります

 以上がrosaの構文です。

 ちなみに、同名ラベルが複数あったとき、その本体はちゃんと出現順で保持されています。

ライブラリ使用法

 Common Lispのライブラリなので、$ ros install t-sin/rosaで導入でき、REPLで(ql:quickload :rosa)するとすぐに使用できます。

パースする

 パースする関数はperuseです。Rosaの入力はストリームで渡してください。結果はハッシュテーブルで返ってきます。

(with-input-from-string (in ":label hogehoge")
  (rosa:peruse in))
; => #<hash-table ...>

 もしすぐに結果が見たければ、peruse-as-plistを使うとplistで結果が返ってきます。

コマンドライン使用法

 だんだんめんどくさくなってきたので簡単に。

 機能としては以下の三つのことができます:

  1. 入力をパースして、ラベルのリストを出力する
  2. 入力をパースして、指定したラベルのテキストを出力する
  3. 入力をパースして、入力中の全ラベル - テキストを出力する

 それぞれ出力の形式をオプションで選べます。

 詳細はREADMEを見てください。

ラベル一蘭を出す

$ cat hoge.txt | rosa index
ラベルが
ズラーっと
出る

指定したラベルの内容を出す

$ cat hoge.txt | rosa pick title -j
"hogeタイトル"

全構造を出す

$ cat hoge.txt | rosa pick title -y
# YAMLで全構造がずらーっとでる

そのほかのユースケース

 テキストファイルのメタデータを表現する以外にも、ラベル - テキストの構造がkey-valueっぽいので、エスケープシーケンスを駆使すれば、デキスト版key-value storeとして使えるような気がしました。非効率だけど。

 あと、仕事では一度この使い方をしたことがあるのですが、ウェブAPIのフォームの値をテキストファイルにrosaの形式で書いておけば、それを読み込むプログラムを(Common Lispで)つくりやすかったです。ある種key-value storeとして使ったとも言える。

今後の発展

 いまは以下のようなのをぼんやり考えています:

  • quicklispに登録しちゃう!?
  • パフォーマンス測定
    • 巨大ファイルだいじょうぶ?
    • 比較対象あれば(だれか教えて!)
  • ラベルの文字数制限する?
  • テストの実装言語非依存化?
  • 別の言語で実装??

 あと、趣味を改善するプロジェクトとして次は、日本語の文章を表現するためのマークアップ言語hearnを考え中なので、こちらもやらねば。

github.com

*1:タイトルやいつ書いたかなどは、ファイル名やパスやファイル更新日時で表現は可能です。でも、気分で変更することもあるので、それらに依存してほしくない。それがファイルの内容に残しておきたい理由です。