すべてのプログラミング言語をLaTeXで書こう：DocStripプログラムの紹介

# DocStripプログラムとは？
DocStripプログラムとは，ソースファイルから特定の部分だけを抜き出す機能を実装したプログラムのことです．この機能により1つのソースファイルからソースコードとそのドキュメントを作成することができます．ドキュメント作成にはTeX/LaTeXを使用します．

言葉で説明するよりも，実際の例を見た方が早いでしょう．次の節に実行例を載せておきます．

なお，既にDocStripプログラムを使ったことがある方にお伝えしたい事として，ここで紹介するコードでは**`.ins`ファイルが不要になります**[^1]．

[^1]: 何を言っているかわからない方は無視しても問題ありません．

# DocStripプログラムの実行例

## 実行環境
*   Ubuntu 20.04
*   TeX Live 2019

## サンプルコード
まず，次の`sample-code.dtx`を保存してください．

<details>
<summary>`sample-code.dtx`を表示する</summary>
<div>

```latex:sample-code.dtx
% \iffalse
%<*driver>
\begingroup
\input l3docstrip
\askforoverwritefalse
\keepsilent
\nopreamble
\nopostamble
\generate{\file{trapezoidal-rule.c}{\from{\jobname.dtx}{code}}}
\endgroup
\documentclass{ltjltxdoc}
\usepackage{amsmath,minted,tcolorbox,tikz,hyperref}
\newminted{c}{bgcolor=gray!10, firstnumber=last, linenos}
\renewcommand{\figurename}{図}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{数値積分}
% \maketitle
% \tableofcontents
% \newpage
%
% \section{台形公式}
%
% \subsection{導出}
% ^^A defining f(x)
% \def\myfunction{{0.3*(\x^3 - 7.6*\x^2 + 18.3*\x) - 2.5}}
%
% \begin{figure}[h]
% \centering
% \begin{tikzpicture}[domain=1:4, >=stealth]
% \coordinate (a) at (1,0);
% \coordinate (b) at (4,0);
% \coordinate (fa) at (1,1);
% \coordinate (fb) at (4,2.2);
% \filldraw[fill=blue!10!white] (a) -- plot (\x, \myfunction) -- (b);
% \draw[blue, very thick] plot (\x, \myfunction);
% \draw[->, thick] (-0.3,0) -- (5,0) node[below] {$x$};
% \draw[->, thick] (0,-0.3) -- (0,3) node[left] {$y$};
% \draw (0,0) node[below left] {$O$};
% \draw (a) node[below] {$a$};
% \draw (b) node[below] {$b$};
% \draw[<-] (1.7,1.8) -- (1.5,2.3) node[above] {$y=f(x)$};
% \draw (2.5,0.7) node {$\displaystyle \int_a^b f(x)\,dx$};
% \end{tikzpicture}
% \caption{$\displaystyle \int_a^b f(x)\,dx$の図形的意味}
% \label{fig:integration}
% \end{figure}
%
% $\forall x\in[a,b]: f(x)\geq0$である関数$f$に対し，
% 定積分$\displaystyle\int_a^b f(x)\,dx$は，曲線$y=f(x)$と$x$軸，
% および2直線$x=a, x=b$で囲まれた図形の面積に等しい（図\ref{fig:integration}）．
%
% \begin{figure}[h]
% \centering
% \begin{tikzpicture}[domain=1:4, >=stealth]
% \filldraw[fill=red!10!white] (a) -- (fa) -- (fb) -- (b);
% \draw[blue, very thick] plot (\x, \myfunction);
% \draw[red, thick] (fa) -- (fb);
% \draw[->, thick] (-0.3,0) -- (5,0) node[below] {$x$};
% \draw[->, thick] (0,-0.3) -- (0,3) node[left] {$y$};
% \draw (0,0) node[below left] {$O$};
% \draw (a) node[below] {$a$};
% \draw (b) node[below] {$b$};
% \draw[<-] (1.7,1.8) -- (1.5,2.3) node[above] {$y=f(x)$};
% \draw (2.5,0.6) node {$\displaystyle (b-a) \frac{f(a)+f(b)}{2}$};
% \end{tikzpicture}
% \caption{一次近似}
% \label{fig:linear-approximation}
% \end{figure}
%
% $f(x)$を一次関数で近似すると，この図形は台形となる（図\ref{fig:linear-approximation}）．
% したがって，この場合，$\displaystyle \int_a^b f(x)\,dx$は次のように近似される．
% \begin{equation}
%   \int_a^b f(x)\,dx \approx (b-a) \frac{f(a)+f(b)}{2}
% \end{equation}
%
% \begin{figure}[h]
% \centering
% \begin{tikzpicture}[domain=1:4, >=stealth]
% \coordinate (a1) at (1.4,0);
% \coordinate (fa1) at (1.4,1.53);
% \coordinate (a2) at (2.2,0);
% \coordinate (fa2) at (2.2,1.75);
% \coordinate (an-1) at (3.7,0);
% \coordinate (fan-1) at (3.7,1.8);
% \filldraw[fill=red!10!white] (a) -- (fa) -- (fa1) -- (a1);
% \filldraw[fill=red!10!white] (a1) -- (fa1) -- (fa2) -- (a2);
% \filldraw[fill=red!10!white] (an-1) -- (fan-1) -- (fb) -- (b);
% \draw[blue, very thick] plot (\x, \myfunction);
% \draw[red, thick] (fa) -- (fa1);
% \draw[red, thick] (fa1) -- (fa2);
% \draw[red, thick] (fan-1) -- (fb);
% \draw[->, thick] (-0.3,0) -- (5,0) node[below] {$x$};
% \draw[->, thick] (0,-0.3) -- (0,3) node[left] {$y$};
% \draw (0,0) node[below left] {$O$};
% \draw (a) node[below] {$a$};
% \draw (b) node[below right] {$b$};
% \draw (a1) node[below] {$a_1$};
% \draw (a2) node[below] {$a_2$};
% \draw (an-1) node[below] {$a_{n-1}$};
% \draw[<-] (1.7,1.8) -- (1.5,2.3) node[above] {$y=f(x)$};
% \draw (3,0.7) node {$\dots\dots$};
% \end{tikzpicture}
% \caption{台形公式}
% \label{fig:trapezoidal-rule}
% \end{figure}
%
% ここで，積分値の精度を上げるために，
% \[
%   a=a_0 < a_1 < \dots < a_n = b
% \]
% として積分区間$[a, b]$を$n$個の閉区間$[a_0,a_1], [a_1,a_2], \dots, [a_{n−1},a_n]$に分け，
% それぞれの区間を同様に一次近似する（図\ref{fig:trapezoidal-rule}）．
%
% このとき，$\displaystyle \int_a^b f(x)\,dx$は次のように近似される．
% \begin{align*}
%   \int_a^b f(x)\,dx
%   = & \int_{a_0}^{a_1} f(x)\,dx
%   +\int_{a_1}^{a_2} f(x)\,dx
%   +\dots
%   +\int_{a_{n-1}}^{a_n} f(x)\,dx \\
%   = & \sum_{k=0}^{n-1} \int_{a_k}^{a_{k+1}} f(x)\,dx \\
%   \approx & \sum_{k=0}^{n-1} (a_{k+1} - a_k) \frac{f(a_k)+f(a_{k+1})}{2}
% \end{align*}
%
% ここで，$n$個に分割された区間の幅が等間隔の場合，
% すなわち，任意の$k=0,1,\dots,n-1$に対し，
% \begin{equation}
%   a_{k+1}-a_k=\frac{b-a}{n}
% \end{equation}
% となる場合を考える．このとき，
% \begin{equation}
%   a_k=a+k\frac{b-a}{n} \quad (k=0,1,2,\dots,n)
% \end{equation}
% であるから，$\displaystyle \int_a^b f(x)\,dx$は次のようになる．
% \begin{align*}
%   \int_a^b f(x)\,d{x}
%   \approx & \sum_{k=0}^{n-1} (a_{k+1} - a_k) \frac{f(a_k)+f(a_{k+1})}{2} \\
%   = & \frac{b-a}{2n}\sum_{k=0}^{n-1}(f(a_k)+f(a_{k+1})) \\
%   = & \frac{b-a}{2n}\left(f(a_0) + 2\sum_{k=1}^{n-1}f(a_k) + f(a_n)\right) \\
%   = & \frac{b-a}{n}\left(\frac{f(a)+f(b)}{2} + \sum_{k=1}^{n-1}f\left(a+k\frac{b-a}{n}\right)\right)
% \end{align*}
%
% 以上をまとめると，次のようになる．
% \begin{tcolorbox}
% \begin{equation}
%   \label{eq:trapezoidal-rule}
%   \int_a^b f(x)\,dx \approx \frac{b-a}{n}
%     \left(\frac{f(a)+f(b)}{2} + \sum_{k=1}^{n-1}f\left(a+k\frac{b-a}{n}\right)\right)
% \end{equation}
% \end{tcolorbox}
%
% \subsection{\texttt{trapezoidal-rule.c}のソースコード}
% \iffalse
%<*code>
% \fi
%
% \subsubsection{ヘッダファイルのインクルード}
% \begin{ccode}
#include <math.h>
#include <stdio.h>
% \end{ccode}
%
% \subsubsection{関数プロトタイプ}
% \begin{ccode}
double integrand(double x);
double trapezoidal_rule(double (*f)(double x), double a, double b, int n);
% \end{ccode}
%
% \subsubsection{main関数}
% 次のコードは，積分区間の分割数$n=1000$のときの$\displaystyle \int_1^3f(x)\,dx$の値を表示する．
% \begin{ccode}
int main(void)
{
  printf("Result: %f\n", trapezoidal_rule(integrand, 1, 3, 1000));
  return 0;
}
% \end{ccode}
%
% \subsubsection{被積分関数の定義}
% \begin{ccode}
double integrand(double x)
{
  return x * sin(x);
}
% \end{ccode}
%
% \subsubsection{台形公式の実装}
% 式(\ref{eq:trapezoidal-rule})を実装する．
% \begin{ccode}
double trapezoidal_rule(double (*f)(double x), double a, double b, int n)
{
  int k;
  double sum = 0.0;
  for (k = 1; k < n; k++)
    sum += f(a + k*(b-a)/n);
  return (b-a)/n * ((f(a) + f(b))/2 + sum);
}
% \end{ccode}
%
% \iffalse
%</code>
% \fi
%
% \section{Simpson公式}
%
% \subsection{導出}
% ...
%
% ^^A End of file `sample-code.dtx'.
```

</div>
</details>

上記の`sample-code.dtx`に対して，次のコマンドを実行してみてください[^2]．

[^2]: 複数回実行する必要があります．

```bash
$ lualatex -shell-escape sample-code.dtx
```

すると次の`trapezoidal-rule.c`と`sample-code.pdf`の2ファイルが生成されるはずです[^3]．

[^3]: その他の形式のファイルも同時に生成されます．不要であれば`$ latexmk -c sample-code.dtx`などで削除して問題ありません．

<details>
<summary>`trapezoidal-rule.c`を表示する</summary>
<div>

```c:trapezoidal-rule.c
#include <math.h>
#include <stdio.h>
double integrand(double x);
double trapezoidal_rule(double (*f)(double x), double a, double b, int n);
int main(void)
{
  printf("Result: %f\n", trapezoidal_rule(integrand, 1, 3, 1000));
  return 0;
}
double integrand(double x)
{
  return x * sin(x);
}
double trapezoidal_rule(double (*f)(double x), double a, double b, int n)
{
  int k;
  double sum = 0.0;
  for (k = 1; k < n; k++)
    sum += f(a + k*(b-a)/n);
  return (b-a)/n * ((f(a) + f(b))/2 + sum);
}
```

</div>
</details>

以下が`sample-code.pdf`の内容です．元ファイルは [ここ](https://drive.google.com/file/d/1i8N9LxVKqprpKuP5BV79vBmCVKsSfzKc/view?usp=sharing "sample-code.pdf") でも見れます．

![combine-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551552/8fd625a2-06fc-5c0e-d504-7b42ab125834.png)

このサンプルコードでは`.dtx`ファイルから`.c`ファイルを生成しましたが，任意のプログラミング言語のファイルを生成できます．

# DocStripプログラムを使うメリット
1.  ソースコードとそのドキュメント・マニュアルを1つのファイルで管理できる
2.  任意のプログラミング言語ファイルを生成できる
3.  ドキュメントに数式や図・表を記載できる

1.について，例えばあるソースコードのドキュメントを作成していたとします．もしそのコードとドキュメントを別ファイルで管理していたら，コードを変更する度にドキュメント側に記載しているコードも変更する必要があります．いちいち変更するのは手間ですし，コードの転記ミスも発生し得ます．しかし，DocStripプログラムではそういったマイナスポイントがありません．

2.について，上記の例で`.dtx`ファイルから`.c`ファイルを生成したように，`.dtx`ファイルからあらゆる言語ファイルを生成できます．さらに言うと，DocStripプログラムは**1つの**`.dtx`ファイルから**複数の異なる言語ファイルを生成できます**．その方法は後で説明します．

3.について，上記例のドキュメント（`.pdf`ファイル）のように，数式や図・表を記載できます．すなわち，`LaTeX`ができることは何でもできます．したがって，索引の自動作成やマクロを組むこともできます．コードについての簡単なメモ書きであれば，コメントとして直接ソースファイルに記述できますが，さすがに上記例のような数式や図は記述できないでしょう[^4]．

[^4]: アスキーアートで表現することも可能かもしれませんが，あまり現実的ではありません．

# テンプレート
`.dtx`ファイルのテンプレートを記載します．各コマンドの役割は次節で説明します．

基本的な編集箇所は以下になるかと思います．

*   `\file{foo}{\from{\jobname.dtx}{bar}}`の`foo`と`bar`
    *   `foo`は生成ファイル名
    *   `bar`はガード
*   `\fi`以降にドキュメント化するコードを記述
*   必要に応じて`\usepackage{baz}`を追加

```tex:template.dtx
% \iffalse
%<*driver>
\begingroup
\input l3docstrip
\askforoverwritefalse
\keepsilent
\nopreamble
\nopostamble
\generate{\file{foo}{\from{\jobname.dtx}{bar}}}
\endgroup
\documentclass{ltjltxdoc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{Template}
% \maketitle
% \tableofcontents
%
% \section{***}
% ...
% ...
% ...
%
% ^^A End of file `template.dtx'.
```

# 各コマンドの役割
ここでは上記`template.dtx`内の各コマンドの役割を説明していきます．より詳しい説明は [docstrip.pdf](http://mirrors.ibiblio.org/CTAN/macros/latex/base/docstrip.pdf) を参照してください．

## `\iffalse`，`\fi`
`\DocInput`から読み込まれるとき，`\iffalse`と`\fi`で囲まれた部分は読み飛ばされます．`\iffalse`，`\fi`で囲まないとエラーが発生します．

## `<*driver>`，`</driver>`
`driver`ガードを設定します．「ガード」という用語については後述します．

実は，`<*driver> ... </driver>`で囲まなくてもエラーが発生することなくコンパイルできますが，生成ファイルに余計な記述が追加されてしまいます．

## `\begingroup`，`\endgroup`
`\begingroup`と`\endgroup`で囲まれた部分をグループ化します．これによりDocStripプログラムの影響がグループ内にしか及ばないようにします．

## `\input l3docstrip`
DocStripプログラムを利用するための記述です．これにより`l3docstrip.tex`内に定義されているコマンドが使えるようになります．

## `\askforoverwritefalse`
生成したいファイル名のファイルが既に存在する場合，上書きしてよいか確認を求められます．ただ，`\askforoverwritefalse`を書いておくことにより，いちいち確認されずに済みます．

逆に上書きの確認をしたいときは`\askforoverwritetrue`を記述しておくとよいでしょう．

## `\keepsilent`
コンパイル時にDocStripプログラムの処理状況がターミナルに表示されますが，`\keepsilent`を記述しておくことでそれが表示されません．

## `\nopreamble`，`\nopostamble`
`\nopreamble`は生成ファイルにプリアンブルを記述しないようにするためのコマンドです．同様に，`\nopostamble`はポストアンブルを記述しないようにするためのコマンドがです．

## `\generate{ ... }`
`\generate`コマンドでファイルを生成します．例えば，次のコードでは`bar`ガードで囲まれた部分を`foo`ファイルに書き込みます．

```tex
\generate{\file{foo}{\from{\jobname.dtx}{bar}}}
```

ここでガードとは`%<...>`のことで，ガードの始まりは`%<*...>`，終わりは`%</...>`で記述します．

例えば，次の`guard-example.dtx`に対して，`$ lualatex guard-example.dtx`を実行します．

<details>
<summary>`guard-example.dtx`を表示する</summary>
<div>

```latex:guard-example.dtx
% \iffalse
%<*driver>
\begingroup
\input l3docstrip
\askforoverwritefalse
\keepsilent
\nopreamble
\nopostamble
\generate{\file{output-file.txt}{\from{\jobname.dtx}{myguard}}}
\endgroup
\documentclass{ltjltxdoc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
%<*myguard>
text1
% comment
text2
%</myguard>
%
% ^^A End of file `guard-example.dtx'.
```

</div>
</details>

すると，次のような`output-file.txt`が生成されます．

```text:output-file.txt
text1
text2
```

また，複数個ファイルを生成することもできます．例えば，次のコードでは`qiita.md`，`sample.py`，`test.cpp`というファイルを生成できます．

```tex
\generate{
  \file{qiita.md} {\from{\jobname.dtx}{qiita}}
  \file{sample.py}{\from{\jobname.dtx}{sample}}
  \file{test.cpp} {\from{\jobname.dtx}{test}}
}
```

## `\jobname`
`\jobname`は`.dtx`ファイル名に置き換わります．例えば，`my-source-code.dtx`であれば`\jobname`は`my-source-code`に置き換わります．

## `\DocInput{ ... }`
`\DocInput{ ... }`の引数で指定されたファイルを読み込みます．その際，`%`をコメントと解釈せずに処理していきます．

つまり，`\DocInput{\jobname.dtx}`とすることにより，自分自身のファイルにあるコメント行もLaTeXのコードとして解釈してドキュメント化してくれます．