第一回
このブログの作成
ようこそ、新ブログ「駄日記」へ!
最近色んな方法で日本語を磨きたいと思って、今回 日本語ブログを作ってみました。せっかく日本語のブログを作るんだったら、日本語プログラミング言語で作ればいいんじゃないの?
ということで、やってみました。 このページは文芸的プログラムで表現しています。最終的に、このページを実行すれば、サイトが出来上がります。
さて、サイトの実装をみましょう。
プログラミング言語「なでしこ」
今回使用している言語はなでしこという日本語プログラミング言語です。詳細はなでしこのサイトに説明されていますが、ひとつ、簡単な例を見てみましょう。
自分は「ダニー」。
相手は「皆様」。
相手に「、こんにちは!{自分}です。」を追加して表示。
出力は、皆様、こんにちは!ダニーです。
となります。
シンプルで分かりやすいプログラミング言語ですね!
しかも連文(文法的「て形」表現)があり、助詞も使い、「日本語らしさ」のある なかなか面白いプログラミング言語だと思います!
世界中のほとんどのプログラミング言語は英語の元で作られているので、「プログラミング=英語」と当たり前のように思ってしまうことが多いのですが、こういう風に違う言語の元で作られるプログラミング言語があると、英語でまったく考えられない表現も出てきますね。あまりメジャーにならないかもしれませんが、こんな言語を作っている方がいるということが、とても嬉しいです。
では、なでしこ言語を使ってサイトを作ってみましょう。
取込みと初期化
まず、これから使う道具の下準備をしましょう。
なでしこはJavaScriptベースで出来ているので、node.jsのパッケージは全部使えます。このサイトは下記のパッケージを使用しています。
- mustache、HTMLテンプレートシステム
- markdown-it、マークダウンからHTML変換
- kansuji、アラビア数字から漢数字への変換
- kanjidate、西暦から和暦への変換
このパッケージをなでしこからアクセスするため、ユーティリティー.jsを取り込む必要があります。
!「./ユーティリティー」を取り込む。
投稿の原書はマークダウンで書いています。それをHTMLに変換して、最後はテンプレートを使って、投稿、固定ページ、タグ一覧などの形にします。ここではそのテンプレートを取り込みます。
テンプレートは全部「テンプレート」というフォルダに入っているので、そのフォルダから取得するユーティリティー関数を作りましょう。
関数 (名前の)ページテンプレートとは
「テンプレート/{名前}」をテンプレート取込。
ここまで。
なでしこの関数定義はこういう感じです。日本語は基本的にSOV型言語ですので、関数名が最後になります。
関数、制御構文などは「ここまで」で終わります。ルビーで言えば「end」と同じようなものですね。個人的に、ただ「です」で終わりたいときもあると思います。特に関数定義と条件分岐で日本語として自然になるのでは?と思いますが、現在は「ここまで」しか使えません。
さぁ、定義した関数を使って、テンプレートを取り込みましょう。
投稿テンプレは「投稿」のページテンプレート。
固定ページテンプレは「固定ページ」のページテンプレート。
タグ一覧は「タグ一覧」のページテンプレート。
投稿一覧は「投稿一覧」のページテンプレート。
全体的投稿一覧は「全体的投稿一覧」のページテンプレート。
フィードテンプレは「フィード」のページテンプレート。
これで準備ができました。
静的ファイルをコピー
一番単純な処理は静的ファイルです。ファイルをそのままコピーするだけです。まず、「静的ファイル」というフォルダ内のファイル全部(再帰的に)を列挙します。ファイルごとに、「静的ファイル」以前の表記を消して、相対パスを取得します。例えば、
/danikki/静的ファイル/hoge/filename.ext
というファイルパスがあるとすれば、
hoge/filename.ext
という風にします。それから「出力」というフォルダ名を先に足します。
出力/hoge/filename.ext
そこに、ファイルをコピーします。コードは下記のようになります。
「静的ファイル」を全ファイル列挙して絶対パスで反復
絶対パスを「静的ファイル/」で区切る。
相対パスはそれ@1。
絶対パスを「出力/{相対パス}」へファイルコピー。
ここまで。
投稿に載っている画像などは「投稿/img」というフォルダに入っているので、そのフォルダからも同じような処理をします。
「投稿/img」を全ファイル列挙して絶対パスで反復
絶対パスを「投稿/img/」で区切る。
相対パスはそれ@1。
絶対パスを「出力/img/{相対パス}」へファイルコピー。
ここまで。
情報抽出
この関数ではタイトルとファイル内容を抽出します。まず、出力のための配列を初期化します。
関数 (ファイルから)情報抽出とは
出力情報は空配列。
投稿ファイルは普通のマークダウンファイルです。最初の行列にレベル1の見出しがあれば、それを投稿名として読み込みます。
最初の行列はレベル2である場合、それをサブタイトルとして読み込んでから、もう一回次の行列からメインタイトルを探してみます。
ファイルを改行で区切って、行列に代入。
行列@0の0から「## 」を文字検索する。
もしそれが1と等しいならば、
行列の0から1を配列取出して、最初行列に代入。
最初行列の1から2だけ文字削除。
出力情報@「投稿名前文」はそれ。
ここまで。
行列@0の0から「# 」を文字検索する。
もしそれが1と等しいならば、
行列の0から1を配列取出して、最初行列に代入。
最初行列の1から2だけ文字削除。
出力情報@「投稿名」はそれ。
ここまで。
行列を改行で配列結合してファイルに代入。
もう一つ、ちょっと変わっている処理があります。このファイルを文芸的に書くためには、マークダウンや説明は 全てなでしこのコメントに入れないとだめです。ただ、普通になでしこのコメント記号「/*」「*/」を使えば、マークダウン にも表示されてしまって、読みにくくなります。それを避けるため、ここで「/*」か「*/」しか書いていない行があれば、マークダウン変換の前に削除します。
コメ開始は「{改行}/*{改行}」
コメ終了は「{改行}*/{改行}」
ファイルのコメ開始で区切って、改行で配列結合。
それのコメ終了で区切って、改行で配列結合。
出力情報@「ソース」はそれ。
最後は、集めた情報を関数から戻します。
出力情報を戻す。
ここまで。
テンプレート適用
メタデータとページ内容が手に入ったら、テンプレートを適用して出力できます。
いちいち「.html」の拡張子を使うのは今の時代かっこ悪いので、フォルダを作ってそこに「index.html」という名前に出力します。
関数 (データをテンプレートでファイル名に)出力とは
「出力/{ファイル名}」のフォルダ作成。
テンプレートを、データでテンプレートレンダー。
それをHTMLに代入。
HTMLを「出力/{ファイル名}/index.html」に保存。
HTMLを戻す。
ここまで。
固定ページの処理
ようやくマークダウン変換でページが作れるようになりました!
まずは固定ページにします。固定ページの場合、投稿日を気にしなくていいし、一覧に入らないので、マークダウン変換とテンプレート出力だけで終わりです。
「固定ページ」を全ファイル列挙してファイルで反復
ファイルを開いて、情報抽出して、ページ情報に代入。
ファイルからファイル名抽出。
それから「.」まで切取ってページ名に代入。
内容はページ情報@「ソース」をMDレンダー。
ページ情報@「内容」は内容。
ページ情報を、固定ページテンプレで、ページ名に出力。
ここまで。
投稿の処理
ここまで来て、やっと 投稿の処理をします。
初期化
投稿は一番複雑な処理になります。あとで「タグ別」と「年別・月別」で一覧したいので、投稿ページを作りながら、そういう参照テーブルにデータを挿入しておきます。ここで、参照テーブルを初期化します。
タグ参照は{}
投稿日参照は{}
全投稿は空配列。
投稿は全部「投稿」というフォルダに入っている「0001」「0002」のようなファイル名で、順番になっています。まず、そのファイルを列挙します。それから、メインページに最新投稿を載せたいので、その索引を調べておきましょう。
投稿列は「投稿/*.md」の全ファイル列挙。
投稿列の要素数から1を引いて最大索引に代入。
最新投稿は投稿列@最大索引。
基本情報取得
ここからは投稿ごとの処理です。少し細かいので、ちょっとずつ進んでいきます。
投稿列を投稿ファイルで反復
投稿ファイルを開いて、情報抽出して、投稿情報に代入。
投稿ファイルからファイル名抽出。
それから「.」まで切取って整数変換して投稿番号に代入。
この辺は固定ページと近いです。ファイルパスからフォルダと拡張子を外して投稿番号を取得します。
次は、前回と次回のリンクを作るため、前と次の番号を計算します。ただし、初回投稿には前回がないし、最新投稿には次回がないので、実際にあるかどうかの確認も必要になります。そのための「ファイル検索」という関数はここで使うけれど、あとで定義します。最後、投稿番号が例えば「1」ならば、「一回目」という漢数字変換をします。
投稿情報@「投稿番号」は投稿番号。
投稿番号に1を足して次回番号に代入。
投稿情報@「次回」は次回番号をファイル検索。
投稿番号から1を引いて前回番号に代入。
投稿情報@「前回」は前回番号をファイル検索。
投稿情報@「回目」は投稿番号の漢数字変換。
タグの処理
タグは「#タグ名」という形で記号しています。正規表現を使って検索します。
投稿情報@「タグ」は空配列。
パターンは「/(^|\s)#(!?(?:\w|[^\u0000-\u007F])+)/g」。
投稿情報@「ソース」をパターンで正規表現マッチ。
それをタグで反復
タグの「#」で区切りして、それ@1をタグに代入。
投稿情報@「タグ」にタグを配列追加。
タグ名を集めてから、タグの処理事態はそんなに難しくないです。タグ参照にタグを検索します。未定義であれば、まず空配列として初期化します。それから、投稿を追加します。
もしタグ参照@タグ=未定義ならば
タグ参照@タグは空配列。
ここまで。
タグ参照@タグに投稿情報を配列追加。
ここまで。
投稿日の処理
まず投稿日と更新日をgitから取得します。「作成日付」と「更新日付」の関数は「ユーティリティー」に実装されています。
投稿情報@「投稿日」は投稿ファイルの作成日付。
投稿情報@「更新日」は投稿ファイルの更新日付。
次は投稿日変換です。これは一番ややこしい処理になります。投稿日が定義されている場合、和暦に変換します。
もし、投稿情報@「投稿日」≠未定義ならば、
和暦投稿日は投稿情報@「投稿日」の元号変換。
戻ってきた値は[年、月、日]という配列になるので、それから変数に入れておきます。
年は和暦投稿日@0。
月は和暦投稿日@1。
日は和暦投稿日@2。
単純にメタデータに抽出します。
投稿情報@「年」は年。
投稿情報@「月」は月。
投稿情報@「日」は日。
次は、投稿日参照に抽出しましょう。投稿日参照は二段階ハッシュマップという形で、「年」で参照をしたら、「月」ごとのハッシュマップを取得します。それから「月」で参照したら、その年のその月の投稿を前部、配列で帰っていきます。
タグと同じように初期化済確認をしてから抽出しますが、二段階ハッシュマップになっているため、二段階で初期化する必要があります。
もし投稿日参照@年=未定義ならば
投稿日参照@年は{}。
ここまで。
もし投稿日参照[年][月]=未定義ならば
投稿日参照[年][月]は空配列。
ここまで。
投稿日参照[年][月]に投稿情報を配列追加。
ここまで。
投稿制作と出力
ようやく必要な情報を全て集めました。マークダウン変換をして出力します。あとでフィードに出力するために、全投稿リストにも足します。
メインページに最新投稿を載せたいので、それだけは特別処理があります。リダレクトURLを情報に足して、ファイル名なしで出力します。
投稿情報@「ソース」をMDレンダー。
投稿情報@「内容」はそれ。
投稿情報を、投稿テンプレで、投稿番号に出力。
全投稿に投稿情報を配列追加。
もし、投稿ファイル=最新投稿ならば、
投稿情報@「リダイレクト」は「/{投稿番号}」。
投稿情報を、投稿テンプレで、「」に出力。
ここまで。
ここまで。
投稿原書ファイル検索
前回と次回を調べるためのファイル検索関数です。投稿は全て「0001」、「0002」のように4桁数字のファイル名を使用しています。まず、必要な0を左に足します。
関数 (投稿番号を)ファイル検索とは
文字列版は投稿番号を文字列変換。
数は文字列版の文字数。
もし、数が3以下ならば、
4から数を引いて、追加分に代入。
「0」を追加分でリフレイン。
それに文字列版を追加。
文字列版はそれ。
ここまで。
それから投稿フォルダに検索します。拡張子はなんでも受け取ります。そのため、多数のファイルがある可能性がありますが、そのときは配列の最後だけを戻して、残りを処分します。
ファイルは「投稿/{文字列版}.*」のファイル列挙。
もし、ファイルの要素数が1以上ならば、
ファイルから配列ポップ。
それから「.」まで切取って整数変換して戻す。
ここまで。
ここまで。
一覧表の作成
投稿、固定ページ、静的ファイルが全て出力できました。あとは一覧表だけです。
タグ一覧
まずはタグ一覧です。タグごとに投稿一覧表を作成して、そのタグの投稿数を投稿数参照に保存しておきます。最後に、投稿数参照を参考しながら、タグの一覧表を出力します。
投稿数参照は空配列
タグ参照のハッシュキー列挙をタグで反復
投稿はタグ参照@タグ。
タグ情報は{
「タイトル」:タグ、
「年を表示」:はい、
「月を表示」:はい、
「投稿」: 投稿}
タグ情報を、投稿一覧で、タグに出力。
投稿数は投稿の要素数を漢数字変換。
投稿数参照に{「タグ」: タグ、
「投稿数」:投稿数}を配列追加。
ここまで。
{「タグ」:投稿数参照}をタグ一覧で、「タグ一覧」に出力。
年別・月別一覧
次は年別・月別の一覧です。タグ一覧とは方法はほとんど一緒ですが、年と月で二段階になっているので、二段階反復をします。
月一覧は空配列。
投稿日参照のハッシュキー列挙を年で反復
年参照は投稿日参照@年。
月別情報は空配列。
年参照のハッシュキー列挙を月で反復
タイトルは[年、月]を「」で配列結合。
投稿は年参照@月。
日付情報は{
「タイトル」:タイトル、
「投稿」: 投稿}
日付情報を、投稿一覧で、「{年}{月}」に出力。
投稿数は投稿の要素数を漢数字変換。
月別情報に{「節」: 月、
「投稿」: 投稿、
「投稿数」:投稿数}を配列追加。
ここまで。
日付情報は{「タイトル」:年、「節」:月別情報}。
日付情報を、投稿一覧で、年に出力。
月一覧に{「年」:年、「月」:月別情報}を配列追加。
ここまで。
全体一覧情報は{「年」:月一覧}。
全体一覧ファイル名は「年別・月別投稿一覧」。
全体一覧情報を全体的投稿一覧で、全体一覧ファイル名に出力。
Atomフィード
最後はRSS/Atomフィードの作成です。もし読者がこのブログをフォローしたい時はフィードリーダーでこのフィードを購読すると、新しい投稿がある時すぐわかるようになります。
今日をISO変換して、本日に代入。
フィード情報は{
「更新時間」:本日、
「投稿」: 全投稿}。
フィードテンプレを、フィード情報でテンプレートレンダー。
それを「出力/atom.xml」に保存。
まとめ
以上、このサイトの作成スクリプトでした!
少し長くなってしまいましたが、日本語プログラミング言語で、ただの習作ではない、実用的なプロジェクトができると面白いかなと思って書いてみました。
この件に関してご意見があれば、@danielpwrightの方にツイートください! このブログを書く理由の一つは自分の日本語の練習のためでもあるので、もし何か間違いや修正点があれば、教えていただけると助かります。
よろしくお願いします。