個人的なメモです。
要件の検討
- dokuwiki ではセルは結合できるけれど、それは変換できなくても仕方がないものとする
- dokuwikiでは行毎にセルの左右寄せを変えることができるけれど、markdownでは列単位でしかできないので、2行目の中寄せ・右寄せが後続の行でも使われるものとする
入出力の形式
入力
- 見出し行は
/^(\^[^^]*)+\^$/ - 後続行は
/^(\|[^\|]*)+\|$/- 繰り返しの回数は見出し行と同じでなければならない(はずなのだが簡単のためテストはしない)
- 最初の後続行(表の第2行)について各セル(
|で分割した各要素)が:-
/^␣␣.*␣␣$/ならば当該列は中寄せに - そうではなく、
/^␣␣/ならば当該列は右寄せに - そうではなければ当該列は左寄せに
-
(ここでスペースはわかりにくいので ␣ ␣ で代えました)
出力
- 見出し行は
|を\|に置換してから^を|に置換して出力(以下、見出し変換と呼ぶ) - 後続行はそのまま出力
- ただし、最初の後続行を出力する前に、左右中寄せ指定を出力
分析
状態遷移図
種類の違う行を受け取ったときにプログラムの挙動が違うので、抜けがないように考えてみます。
こういうのを有限状態オートマトンといいます。
状態遷移表
慣れれば状態遷移図を見ながらコーディングできますが、ねんのため状態×入力の表にしてみましょう。
| 見出し行 | 後続行 | その他の行 | |
|---|---|---|---|
| 初期状態 | 見出し変換出力; 見出し認識状態へ | そのまま出力 | そのまま出力 |
| 見出し認識状態 | そのまま出力; 初期状態へ | 左右中寄せ指定出力; そのまま出力; 後続行認識状態へ | そのまま出力; 初期状態へ |
| 後続行認識状態 | そのまま出力; 初期状態へ | そのまま出力 | そのまま出力; 初期状態へ |
あとはガリガリコーディング
たとえば JavaScript ならなんかこんな感じで動く。
function convert(){
let val = document.getElementById("text-area").value;
let lines = val.split(/\r\n|\n/);
let text = [];
let state = 0;
for (var line of lines) {
if (state === 0) {
if (/^(\^[^^]*)+\^$/.test(line)) {
line = line.replace(/\|/g, "\\|");
line = line.replace(/\^/g, "|");
state = 1;
}
} else if (state === 1) {
if (/^(\|[^\|]*)+\|$/.test(line)) {
let cols = line.split(/\|/);
cols.shift();
cols.pop();
let colspec = cols.map(function (col){
if (/^ \.* $/.test(col)) {
return ':---:';
} else if (/^ /.test(col)) {
return '---:';
} else {
return ':---';
}
});
colspec.unshift('');
colspec.push('');
text.push(colspec.join('|'));
state = 1;
} else {
state = 0;
}
} else if (state === 2) {
if (!(/^(\|[^\|]*)+\|$/.test(line))) {
state = 0;
}
}
line = line.replace(/^======\s*(.*)\s*======$/, "# $1");
line = line.replace(/^=====\s*(.*)\s*=====$/, "## $1");
line = line.replace(/^====\s*(.*)\s*====$/, "### $1");
line = line.replace(/^===\s*(.*)\s*===$/, "#### $1");
line = line.replace(/^==\s*(.*)\s*==$/, "##### $1");
// 一行に複数リンクがあるときのバグは直したよ
line = line.replace(/\[\[([^[\|]*)\|([^[]*)\]\]/g, "[$2]($1)");
text.push(line);
}
let txt = document.getElementById("output");
txt.innerHTML = text.join("\r\n");
}