macrodown -mlが使えるml-
TRANSCRIPT
背景にある問題意識
TEX言語(とその上の LATEX)
😄 ユーザが柔軟にマクロ定義できる
😫 構文も意味論も闇
• 構文木という概念がない
• 字句解析と構文解析と評価が同時進行
• “合流性” なし
• エラーの原因がわかりづらすぎる
出力 Macrodown インタプリタ
文書本体 (.mcrd)
基本的には文書用のマクロ集を文書と別に書く
文書用マクロ集 (.mcrdh)
マクロ定義の方法
ML風の構文 で書ける
LATEX+Markdown 風の 構文で書ける
• プログラム階層
- 主にマクロ定義に使う
- マクロ集 (.mcrdh ) ではこの階層が主体
- ML 風の構文
• テキスト階層
- 主にマークアップに使う
- 文書本体 (.mcrd ) ではこの階層が主体
- LATEX+Markdown 風の構文
構文の “階層”
文書本体 main.mcrd
文書用マクロ集 first.mcrdh
例1
Hello, \mcrd;!
let \mcrd = {Macrodown} Macrodown
プログラム階層
テキスト階層
文書本体 main.mcrd
文書用マクロ集 first.mcrdh
例1
Hello, \mcrd;!
let \mcrd = {Macrodown} Macrodown
プログラム階層
テキスト階層
入れ子の テキスト階層
文書本体 main.mcrd
文書用マクロ集 first.mcrdh
例1
$ macrodown first.mcrdh main.mcrd –o output.txt
Hello, \mcrd;!
let \mcrd = {Macrodown} Macrodown
出力
output.txt
文書本体 main.mcrd
文書用マクロ集 first.mcrdh
例1
Hello, \mcrd;!
Hello, Macrodown!
$ macrodown first.mcrdh main.mcrd –o output.txt
let \mcrd = {Macrodown} Macrodown
例2:文字列引数をとる
let \md s t = {Ma} ^ s ^ {r} ^ t ^ {down}
Hello, Macrodown! Good bye, Markdown!
文字列結合
Ma down r
Hello, \md{c}{o}! Good bye, \md{}{k}!
例2:文字列引数をとる
let \md s t = {Ma@s;r@t;down}
Hello, Macrodown! Good bye, Markdown!
Hello, \md{c}{o}! Good bye, \md{}{k}!
Ma@s;r@t;down
テキスト階層内変数参照
例2:文字列引数をとる
! [ERROR AT TYPECHECKER] at line 1, characters 7-13: this expression has type string -> string but is expected of type string.
let \md s t = {Ma@s;r@t;down}
Hello, \md{c}! ^^^^^^
型が合わないと……
Ma@s;r@t;down
例3:整数値引数をとる
let repeat n s = if n <= 0 then {} else s ^ (repeat (n - 1) s) let \repeat = repeat
letは再帰
\repeat(3){foo}! 3
例3:整数値引数をとる
let repeat n s = if n <= 0 then {} else s ^ (repeat (n - 1) s) let \repeat = repeat
foofoofoo!
letは再帰
テキスト階層の プログラム引数は
( … ) でくくる
例3:整数値引数をとる
let \repeat n s = if n <= 0 then {} else {@s;\repeat(n - 1){@s;}}
foofoofoo!
\repeat(3){foo}! 3
@s;\repeat( ){@s;}
1 軽量リスト記法
\div-each{|hoge|piyo|fuga|moge|}
<div>hoge</div> <div>piyo</div> <div>fuga</div> <div>moge</div>
文字列リストを処理するとき,いちいち
\div-each( [{hoge}; {piyo}; {fuga}; {moge}] );
などと書くのは読み書きに不便
1 軽量リスト記法
\div-each{|hoge|piyo|fuga|moge|}
<div>hoge</div> <div>piyo</div> <div>fuga</div> <div>moge</div>
let \div-each [] = {} | (hd :: tl) = {<div>@hd;</div> \div-each(tl);}
string list 型
<div>@hd;</div> \div-each(tl); tl
2 多重軽量リスト記法
\listing{ * 東京
** 駒場
** 本郷
** 弥生
** 浅野
* 千葉
** 柏
}
<ul> <li>東京
<ul> <li>駒場</li> <li>本郷</li> <li>弥生</li> <li>浅野</li> </ul></li> <li>千葉
<ul> <li>柏</li> </ul></li> </ul>
2 多重軽量リスト記法
\listing{ * 東京
** 駒場
** 本郷
** 弥生
** 浅野
* 千葉
** 柏
}
ヴァリアント型で実現
itemize = Item of string * itemize list
2 多重軽量リスト記法
\listing{ * 東京
** 駒場
** 本郷
** 弥生
** 浅野
* 千葉
** 柏
}
ヴァリアント型で実現
Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])
itemize = Item of string * itemize list
2 多重軽量リスト記法
\listing{ * 東京
** 駒場
** 本郷
** 弥生
** 浅野
* 千葉
** 柏
}
ヴァリアント型で実現
Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])
itemize = Item of string * itemize list
2 多重軽量リスト記法
\listing{ * 東京
** 駒場
** 本郷
** 弥生
** 浅野
* 千葉
** 柏
}
ヴァリアント型で実現
Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])
itemize = Item of string * itemize list
2 多重軽量リスト記法
Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])
あとはこの値を
パターンマッチで
再帰的に出力に変換
するだけ
多重リストが
簡潔に書けてしかも
静的型に取り込めている
\div .test #sentence { The quick brown fox jumps over the lazy dog. }
3 クラス名・ID名オプション
<div class="test" id="sentence"> The quick brown fox jumps over the lazy dog. </div>
余剰(省略可能)な引数
HTML を強く意識した機能
現在仕様改定を計画中のため,詳細は割愛
(’a maybe 型 の値として扱いたい)
4 コード記述用領域
\code `\hoge;@var;` \display-C ``` #include <stdio.h> int main(void) { printf("Hello Code Area\n"); return 0; } ``` \haskell ``a `elem` xs``
“文字通り” に書ける領域
5 出力時インデント
let \p content = {<p>\deeper{@content;}</p>} <p>\deeper{@content;}</p>
出力のインデントを深くし, 前後に改行を補う
5 出力時インデント
let \p content = {<p>\deeper{@content;}</p>}
<p> Time flies like an arrow. </p>
\p{Time flies like an arrow.}
<p>\deeper{@content;}</p>
\deeper が改行とインデントを挿入
\p{␣Time flies like an arrow.␣}
5 出力時インデント
let \p content = {<p>\deeper{@content;}</p>}
<p> Time flies like an arrow. </p>
<p>\deeper{@content;}</p>
{ 直後と } 直前は 空白と改行を無視
5 出力時インデント
let \p content = {<p>\deeper{@content;}</p>}
<p> Time flies like an arrow. </p>
\p{↲ ␣␣Time flies like an arrow.↲ }
<p>\deeper{@content;}</p>
5 出力時インデント
<p> Time flies like an arrow. Fruit flies like a banana. </p>
\p{↲ ␣␣Time flies like an arrow.↲ ␣␣Fruit flies like a banana.↲ }
途中の改行は 無視されない
6(準備:破壊的代入)
let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}
x : int ref
\document{ \title{MLstudy} }{ \maketitle; … }
ここでタイトルを 指定して
ここでタイトルの 文字列を出力に使いたい
6 擬似遅延
やや強引な例:
タイトル文字列を使い回したい
<!DOCTYPE html> <html> <head> … <title>MLstudy</title> … </head> <body> <div class="title">MLstudy</div> … </body> </html>
6 擬似遅延
let-mutable title <- {} let \maketitle = let ttl = !title in {<div class="title">@ttl;</div>} let \title ttl = title <- ttl before {<title>@ttl;</title>}
\document{ \title{MLstudy} }{ \maketitle; … }
<div class="title">@ttl;</div>
<title>@ttl;</title>
6 擬似遅延
let-mutable title <- {} let \maketitle = let ttl = !title in {<div class="title">@ttl;</div>} let \title ttl = title <- ttl before {<title>@ttl;</title>}
\document{ \title{MLstudy} }{ \maketitle; … }
<div class="title">@ttl;</div>
<title>@ttl;</title>
即座に 評価されて
破滅!
6 擬似遅延
let-mutable title <- {} let \maketitle () = let ttl = !title in {<div class="title">@ttl;</div>} let \title ttl = title <- ttl before {<title>@ttl;</title>}
\document{ \title{MLstudy} }{ \maketitle(()); … }
<div class="title">@ttl;</div>
<title>@ttl;</title>
普通の 回避方法: () でサンク
()
6 擬似遅延
let-mutable title <- {} let \maketitle () = let ttl = !title in {<div class="title">@ttl;</div>} let \title ttl = title <- ttl before {<title>@ttl;</title>}
\document{ \title{MLstudy} }{ \maketitle(()); … }
<div class="title">@ttl;</div>
<title>@ttl;</title>
😫 実装方法に依存した,
マークアップ上意味のない () が現れる……
()
6 擬似遅延
6 擬似遅延 let-lazy
let-mutable title <- {} let-lazy \maketitle = let ttl = !title in {<div class="title">@ttl;</div>} let \title ttl = title <- ttl before {<title>@ttl;</title>}
\document{ \title{MLstudy} }{ \maketitle; … }
<div class="title">@ttl;</div>
<title>@ttl;</title>
😄 エレガントな表記で
意図通り動く。
let-lazy を使うと
最終値参照が必要な場面
\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。
} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …
}
例: 相互参照機能
最終値参照が必要な場面
\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。
} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …
}
例: 相互参照機能
最終値参照が必要な場面
相互参照機能では一般に
参照先が後ろにあるかもしれない
しかし章番号を参照したいなら
後ろにある章番号を手前で使う必要がある
ちなみに LATEX では
こういう場合は複数回タイプセットする
Macrodown はそういう力技はやらない主義(?)
⇒ 代わりに邪道を往く
最終値参照の実現
解決法:
最終値参照用のハッシュテーブルを備えつける
• iroha を参照したい箇所には
「ここに iroha に対応する文字列が後で入るよ」
という “印” を書き込む
• iroha の箇所では iroha とそれに対応する文字列を
ハッシュテーブルに追加する
最終値参照の実現
解決法:
最終値参照用のハッシュテーブルを備えつける
• iroha を参照したい箇所には
「ここに iroha に対応する文字列が後で入るよ」
という “印” を書き込む
• iroha の箇所では iroha とそれに対応する文字列を
ハッシュテーブルに追加する
• 最後の出力時に “印” をハッシュテーブルをもとに
具体的な文字列で置き換える
\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。
} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …
}
最終値参照
最終的に iroha に対応する 文字列に置き換える “印”
!! {iroha}
iroha と対応させる章番号の文字列 {2} を ハッシュテーブルに追加する:
new-global-hash {iroha} <<- {2}
評価終了=出力直前のイメージ
<section>\deeper{ <h1>第1章 はじめに</h1> <p>\deeper{ いろは歌を第(!! {iroha})章に掲げる。
}</p> }</section> <section>\deeper{ <h1 id=>第2章 いろは歌</h1> <p>\deeper{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … }</p>
}</section>
“印” が文字列内に 残っている
評価終了時,ハッシュテーブル内
では iroha と {2} が対応
出力
<section> ␣␣<h1>第1章 はじめに</h1> ␣␣<p> ␣␣␣␣いろは歌を第2章に掲げる。
␣␣</p> </section> <section> ␣␣<h1>第2章 いろは歌</h1> ␣␣<p> ␣␣␣␣色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 ␣␣␣␣… ␣␣</p>
</section>
iroha と {2} の対応が ハッシュテーブルにあるので 書き換えて出力
最終値参照の闇1
\section { いろは歌を第\ref{aiueo}章に掲げる。
} \section #iroha { 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …
}
存在しない “印” を 書いてしまっていたら……?
最終値参照の闇1
\section { いろは歌を第\ref{aiueo}章に掲げる。
} \section #iroha { 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …
}
存在しない “印” を 書いてしまっていたら……?
! [ERROR AT EVALUATOR] undefined global hash key ’aiueo’.
実行時エラー!(事前にはわからない)
最終値参照の闇2
(string-sub (!! {test}) 0 3) ^ {/} ^ ( new-global-hash {test} <<- {Macrodown} before !! {test} )
最終値参照の結果の文字列を具体的に加工するようなプログラムを書くと……
⇒ 予想がつかないことになりがち(説明は割愛)
上の例は {Mac/Macrodown} になると
思いがちだが実際にはならない
test
test test Macrodown
/
HTML
PDF LATEXコード
Macrodown インタプリタ
LATEX エンジン
(+ドライバ) LATEX出力用
ライブラリ
HTML出力用 ライブラリ
役割としてはプリプロセッサ
実用上の Macrodown
入力
PDF LATEXコード
Macrodown → LATEX → PDF
Macrodown インタプリタ
LATEX エンジン
(+ドライバ)
入力
LATEX出力用
ライブラリ
実用上の Macrodown
HTML
PDF LATEXコード
Macrodown インタプリタ
LATEX エンジン
(+ドライバ) LATEX出力用
ライブラリ
HTML出力用 ライブラリ
実用上の Macrodown
入力
言語 X コード
Macrodown インタプリタ
言語 X 出力用 ライブラリ
出力形式に依存せず共通の方式でマークアップ
ユーザ自身がライブラリを書いて用意すれば
あらゆる形式に変換可能
実用上の Macrodown
入力
まとめ
Macrodown は
• MLが使える,ラップ用マークアップ言語
• 静的型検査ができる
(マークアップと型システムは意外に相性良好)
• マークアップ特化機能も充実
- 軽量リスト記法 - インデント出力
- 擬似遅延 - 最終値参照
- クラス・ID名オプション
今後の発展
• 浮動小数点数やレコード型の実装
• 軽量数式記法(実用上かなり重要)
• 軽量テーブル記法
• ライブラリの整備
• 仕様書を書き起こす
• 最終値参照の闇を軽減する型システム検討
pure-string <: string
\deeper ・改行・!! を含まない純粋な文字列
実装
現在の実装 (ver. 1.00δ) は
http://github.com/gfngfn/Macrodown
で公開しています(正式リリースはまだ)
Pull request 大歓迎!(泣いて喜びます)