WINI & cal: perlベースの新しい簡易マークアップ言語WINIで来年のカレンダーを作りましょう！

この記事は、<a href="https://qiita.com/advent-calendar/2019/perl">Perl Advent Calendar 2019</a>の19日目の記事です。なんか埋まってなかったので書かせていただきました。18日目は[xtetsuji](https://qiita.com/xtetsuji)さんの「ここ数年の間で便利に使っているPerlの正規表現〜2019年版〜」でした。


# はじめに

WINIは2019年に筆者によって開発・公開された、新しい簡易マークアップ言語です。

WINIを扱うツールwini.pmは、perlのモジュールであり、またHTML5形式のファイルを生成するためのフィルタコマンドとして使用できるように作られています。wikiやmarkdownに似た書式で原稿を作り、これをwini.pmの標準入力に与えると、いい感じにHTML5形式の出力が得られるのです。

wini.pmは[github](https://github.com/Koji-Doi/wini)にて公開されています。MITライセンスを採用していて、誰もが無料でダウンロードでき、営利だろうと非営利だろうと自由に利用できます。

詳しいことは[別記事『WINI ━ WIKIやMARKDOWNを超えた新しいマークアップ言語』](https://qiita.com/doikoji/items/f6139b7d91b48e50dcef)を参照していただくとして、ここではパイプを介した他ツールとの連携、および簡単なperlスクリプト構築例として、「来年のカレンダーを作る」をお題にWINIの応用方法を紹介したいと思います。

# パイプでのツール連携

暦計算を自分で真面目にやるとなるとそれなりに面倒なので、unix-like OSに標準で備わっているコマンドcalに任せることにしましょう。これをパイプを通して加工してwini.pmに渡してやれば、カレンダーがHTML5形式で吐き出されるというわけです。

## cal

コマンドラインに月・年を指定すると、calはその月のカレンダーを標準出力に出します。

```
$ cal 1 2020       
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
```


## WINI形式ではこうなる

おおざっぱに言えば、7列5行のテーブルを組んで、1から31までの数字を入れていけばいいわけです。WINIは表作成が特に得意です。というか、markdownやwikiでは表作成機能が足りなくて不便なので補完する目的で作られたのがWINIなのです。

WINI形式でカレンダーを表現するとすると、次のような感じになります。

```
|-     January 2020 | &r             |
|!! Su | Mo | Tu | We | Th | Fr | Sa |
|      |    |    |  1 |  2 |  3 |  4 |
|    5 |  6 |  7 |  8 |  9 | 10 | 11 |
|   12 | 13 | 14 | 15 | 16 | 17 | 18 |
|   19 | 20 | 21 | 22 | 23 | 24 | 25 |
|   26 | 27 | 28 | 29 | 30 | 31 |    |
```

## cal → perlで前処理 → wini.pm

calの出力を上記のように変換するために、perlのワンライナーをプリプロセッサとして設定します。そして、その出力をwini.pmへの入力とします。大体次のような感じになるでしょう。

```
cal 1 2020 | \
perl -nle '/\w/ or next; \
$l=$_; $.==1 and print(qq{|- $l | \&r |}), next; \
printf(($.==2)?q{|!! }:q{| }); \
print(join(q{ | }, map {$x=$_*3; substr($l, $x, 2) } 0..6), qq{ |})' \
| wini.pm > ~/public_html/cal.html

# 見やすさを考えて適宜改行を入れています。
```

できたcal.htmlをブラウザで表示させてみます。
![cal1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/383429/819f6194-dc88-c9cf-6c87-a04cfef827c3.png)


# カスタマイズ

パイプ処理の手順は確認できました。しかし、もう少し色気が欲しいところです。

## カスタマイズ例の１
とりあえず、日曜日は赤字にしてみましょう。

```
cal 1 2020 | perl -nle '/\w/ or next; \
$l=$_; $.==1 and print(qq{|- $l | \&r |}), next; \
printf(($.==2)?q{|!!...f-r }:q{| }); \
print(join(q{ | }, map {$x=$_*3; substr($l, $x, 2) } 0..6), qq{ |})' \
|./wini.pm --whole > ~/public_html/cal2.html
```

この例では、こんな感じのWINIデータができて、

```
|-     January 2020     | &r |
|!!...f-r Su | Mo | Tu | We | Th | Fr | Sa |
|    |    |    |  1 |  2 |  3 |  4 |
|  5 |  6 |  7 |  8 |  9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |  |
```

こういう出力になります。
![cal2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/383429/4bd74ac7-6e9f-4cfa-3d8f-1b72b4e3e10b.png)

## カスタマイズ例の２

やっぱ、罫線が欲しいかな。

```
 cal 1 2020 | perl -nle '/\w/ or next; \
$l=$_; $.==1 and print(qq{|- $l | \&r border="1" |}), next; \
printf(($.==2)?q{|!!...f-r }:q{| }); \
print(join(q{ | }, map {$x=$_*3; substr($l, $x, 2) } 0..6), qq{ |})' \
| ./wini.pm --whole > ~/public_html/cal3.html
```

こうなって：

```
|-     January 2020     | &r border="1" |
|!!...f-r Su | Mo | Tu | We | Th | Fr | Sa |
|    |    |    |  1 |  2 |  3 |  4 |
|  5 |  6 |  7 |  8 |  9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |  |
```

こういう出力になります。

![cal3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/383429/a7109cc3-c0cd-dfe3-a7bb-f137bc63f779.png)

## カスタマイズ例の３

ここまで来たら、「メモが書き込めるカレンダー」の印刷を。
行幅・列幅を広げ、日付の数値はセルの右上に配置することにしましょう。

```
cal 1 2020 | perl -nle '/\w/ or next; \
$l=$_; $.==1 and print(qq{|- $l | \&r border="1" |}), next; \
printf(($.==2)?q{|!!$100&t...f-r }:q{|&t$$100 }); \
print(join(q{ |&t$100 }, map {$x=$_*3; substr($l, $x, 2) } 0..6), qq{ |})' \
| ./wini.pm --whole > ~/public_html/cal4.html
```

こうなって：

```
|-     January 2020     | &r border="1" |
|!!$100&t...f-r Su |&t$100 Mo |&t$100 Tu |&t$100 We |&t$100 Th |&t$100 Fr |&t$100 Sa |
|&t$$100    |&t$100    |&t$100    |&t$100  1 |&t$100  2 |&t$100  3 |&t$100  4 |
|&t$$100  5 |&t$100  6 |&t$100  7 |&t$100  8 |&t$100  9 |&t$100 10 |&t$100 11 |
|&t$$100 12 |&t$100 13 |&t$100 14 |&t$100 15 |&t$100 16 |&t$100 17 |&t$100 18 |
|&t$$100 19 |&t$100 20 |&t$100 21 |&t$100 22 |&t$100 23 |&t$100 24 |&t$100 25 |
|&t$$100 26 |&t$100 27 |&t$100 28 |&t$100 29 |&t$100 30 |&t$100 31 |&t$100  |
```

こういう出力になります。
![cal4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/383429/377e09b0-8e91-4494-5fc1-a88ebcac8d2a.png)

# まとめ

カレンダーを素材として、wini.pmなるツールのコマンドラインツールとしての使い方の簡単な例を紹介しました。

wikiやmarkdownが一応のデファクトスタンダードの位置を獲得しているといえる状況でなぜ敢えてこのような「車輪の再発明」を行ったか。表作成機能の貧弱さと、既存のシステムの設定や操作がどうにも煩雑であったことへの不満があったからでした。

既存マークアップ言語を私なりに研究してこしらえた新しいマークアップ文法と、wini.pmというたった一つのファイルで全てを賄うシンプルさ。不満はかなりの部分で解決できたように思っています。

wini.pmは、表形式文書を日常的に繰り返し増産するような仕事に向いていると思います。典型例が日報ですね。ここでは詳しく説明しませんでしたが、rowspanやcolspanを激しく使いまくった複雑な形式の表をwiniではすっきりと書き下すことが出来ます。これを活かしてテンプレートを作っておき、テキストは別途編集して流し込むスクリプト（perlで100行はいかないでしょう）を書けば、専用日誌システムが直ぐに出来上がります。

またHTML5専用ということでclassやIDを活用できるようにしています。適切なCSSを用意すれば、入力の簡単さと見た目の美しさを容易に両立できることでしょう。

ぜひとも興味をもっていただいて、公私の業務の効率化に役立てていただければ嬉しいです。

20日の<a href="https://qiita.com/advent-calendar/2019/perl">Perl Advent Calendar 2019</a>は[ktat](https://qiita.com/ktat)さんによる「[GraphViz::DBI で ER図を吐き出す](https://ktat.hatenadiary.jp/entry/2019/12/20/195705)」です。