この記事はTeX & LaTeX Advent Calendar 2017の24日めのために書きました。
TeXの中でMarkdownを書ける
先月のことなんですが、TeXの、TeXによる、TeXのためのMarkdownパーサをつくました。
- markdown-tex
https://github.com/k16shikano/markdown-tex
TeXで実装されてるので、当然、TeXで書く文書のなかで、シームレスにMarkdownを使えます。
具体的には、こんな感じに、\begin{markdown}
から\end{markdown}
のなかにMarkdown記法が書けます。
\documentclass{article} \usepackage{md} \begin{document} \begin{markdown} # markdown-tex markdown processor in TeX. As inline styles, `tt`, *italic*, and **bold** are available. ## subsection Here is a enumerate list. 1. enumerate 1. in 1. the markdown way This is an itemize list. * this block * will be * itemized And a quotation. > Quote Somethin > Really **Awesome**! \end{markdown} \end{document}
これをふつうにTeXで処理すると、記法から期待されるようなPDFができるわけです。
とはいっても、先月の時点でサポートしていた記法はそんなに多くなくて、これくらい。
- 行頭の「
#
」の個数に応じた見出し化 - 行頭の「
*
」で箇条書き - 行頭の「
<
」でインデント - 行頭の数字で連番箇条書き
- 行頭スペース4個でインデントするとコードブロック化
`..`
で囲むとインライン等幅*..*
で囲むとインラインイタリック**..**
で囲むとインライン強調
これ以外の要素を書きたかったら、いったん\end{markdown}
して、生のTeXを書いてください。TeXなので、組版に関することなら、なんでもできます。
Markdownの中でTeXを書ける
この記事を読んでいる人はみんなTeX Conf 2017に参加しているので既知だと思いますが、念のため補足すると、このMarkdownパーサはTeX Conf 2017で発表した「TeXは軽量マークアップ言語の夢を見るか」のために作ったものです。 この発表では、Markdownをはじめとするマークアップ記法と、TeX(LaTeX)によって得られる表現力との関係について考察しました。 「Markdownについて話すのにMarkdownパーサの実装経験がなければ刺されるかも」という強迫観念から取り組んだ、概念実証のための実装です。
で、TeXのなかでMarkdownを使えるからといってうれしいことはあまりないし、それで済ませるつもりだったですが、せっかくTeXなので、先日ちょっと手を入れて\TeX
くらいは生で書けるようにしました。
その副作用で、コントロールシーケンスや数式も入れられるようになりました!
# markdown-tex markdown processor in TeX. As inline styles, `tt`, *italic*, and **bold** are available. Inline equation in \TeX notation $\int_0^1\frac{1}{x}dx = \infty$. \hfill December 24 2017 ## subsection Here is a enumerate list. 1. enumerate 1. in …
しかし、実はこれは誇大広告で、厳密にいうと2つの条件を満たすマクロやコマンドだけがMarkdown記法の中で使えます。
1つめの制約は、完全展開できる必要があるという点です。
たとえば、\sqrt
は使えません。
これは、latex.ltx
における\sqrt
の定義では\@ifnextchar
が利用されていて、そのなかでは展開されない\futurelet
が使われているからです。
なんで完全展開可能性が必要かというと、Markdown記法の構文解析を簡単にするために、対象となるトークンリストをいったん「完全展開された文字列」へと変換しているからです。
これにはexpl3の\tl_set_rescan:Nnx
を使っています。
その際、TeXの入力を完全展開すると空白文字が集約されてしまってMarkdown記法として構文解析できなくなるので、これらは文字列とみなして完全展開しています。
\tl_set_rescan:Nnx \md_tokens { \char_set_catcode_letter:n { 32 }% SPACE } { \BODY }
これで\md_tokens
変数に「完全展開された文字列」が入るので、それを構文解析し、それをTeXの胃袋に送ってPDFを作らせる、というのが先月までの実装でした。
そんなわけで、この状態でMarkdown記法の中に\TeX
と書くと、このマクロが完全展開されたT\kern -..5ex\hbox E\kern -.\@m
がそのままPDFに出てきてしまう状態でした。
では、Markdown記法の中に\TeX
と書いて期待する出力結果を得るにはどうすればいいでしょうか?
完全展開をやめればよさそうですが、前述したようにスペースの扱いの都合があるので、文字列にしてからパースするという方針は変えがたい。
となると、完全展開された文字列をパースするときにTeXのプリミティブをパースしてコントロールシーケンスに変換し直し、それをTeXの胃袋に送ればいいのではないか?
というわけで、そんなようなコードを追加して、無事に\TeX
のようなマクロがMarkdown記法のなかに埋め込めるようになりました。
\cs_new:Npn \inside_cs:Nnnn #1#2#3#4 { \tl_set:Nn \car_line { \tl_head:n { #4 } } \tl_set:Nn \cdr_line { \tl_tail:n { #4 } } \tl_if_head_eq_charcode:nNTF { #4 } { \sp_letter } { \tl_put_right:Nn #2 { \cs:w #3 \cs_end: } \exp_args:NNnff \parse_to_end_line_with:Nnnn { #1 } { #2 } { } { \cdr_line } } { \tl_set:Nx \so_far { #3\car_line } \exp_args:NNnff \inside_cs:Nnnn { #1 } { #2 } { \so_far } { \cdr_line } } }
ただし、実装をサボっているので、非標準的(?)な利用方法のコントロールシーケンスはパースできません。
たとえば、\hbot{foo}
はいいけど、\hbox to 10pt{foo}
とかはだめ。
これがもう1つの制約です。
今回のオチ
にしても、ここまでにやったことを振り返ると、事実上TeXでTeXをパースしているわけで、むだなことをした感が半端ない……。
- TeX記法の入力をTeXでパースし、トークンリストが作られた(TeXの仕事)
- トークンリストをMarkdown記法としてパースするために、完全展開して文字列化した(
\tl_set_rescan:Nnx
) - その文字列からコントロールシーケンスを取り出すのに、あらためてTeX記法のパーサを実装に追加した(
\cs_new:Npn \inside_cs:Nnnn
)
0 件のコメント:
コメントを投稿