Published on

TeX 入門 1

Authors

NOTE

本記事は, いもあらい。さん1の「苦しみのTeX言語プログラミング」の内容を演習的に行ったものです. 同書が気になる方はBOOTHまたは技術書典よりご購入下さい. また, 本記事では整数, 条件分岐のみに触れます. 繰り返しや展開制御, リスト構造, タプル構造等に関しては, 時間が確保でき次第「TeX 入門 2」として執筆します.

Table of Contents

TeX

TeX とは, Donald Ervin Knuthによって開発された組版システムである. また, 私たちが一般的に使用している LaTeX は TeX 上に構築された文書処理システムである. そのため, 一般に混合して使用されがちな TeX と LaTeX は区別されるべきであり, 本記事は 前者の TeX についての記事である.

Hello, TeX!

まずは, Hello, world! ならぬ Hello, TeX! をする. TeX における標準入出力を行うためのマクロを以下のように定義する.

stdio.tex
\def \ReadStdin(#1){
  {
    \endlinechar = -1
    \global\read 0 to #1
  }
}

\def \WriteStdout(#1){
  \immediate\write 0 {#1}
}

以降では, 上記のファイルを読み込んで \ReadStdin, \WriteStdout を使用する.

Hello, TeX! をするには以下のように記述する.

hello.tex
\input{stdio}

\ReadStdin(\Name)
\WriteStdout(Hello, \Name!)
\end

続いて, 以下のように実行する2. なお, 本記事では LuaTeX を使用する.

$ luatex --halt-on-error hello.tex 
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./hello.tex (./stdio.tex)
\Name =TeX
Hello, TeX!
)
warning  (pdf backend): no pages of output.
Transcript written on hello.log.

上記の実行結果より, 標準入力として \Name = が求められ, TeX と入力すると, 標準出力で Hello, TeX! が出力されていることが分かる.

単なる Hello, world! の場合
hello.tex
\input{stdio}
\WriteStdout(Hello, world!)
\end

実行結果

$ luatex --halt-on-error hello.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./hello.tex (./stdio.tex)
Hello, world!
)
warning  (pdf backend): no pages of output.
Transcript written on hello.log.

プロセッサ構成

TeX の処理系は以下の4つのプロセッサから構成されている.

  • 入力プロセッサ
    • ソースコードの文字列からトークン列を作る.
  • 展開プロセッサ
    • トークン列を必要に応じて展開し, 展開後のトークン列にする.
  • 実行プロセッサ
    • 展開後のトークン列を実行し, 処理系の内部状態を変更したり組版のボックスリストを作る.
  • 出力プロセッサ
    • 組版のボックスリストからページを出力する.

トークンの種類

  • 文字トークン
    • a, 1, @ 等.
  • 制御トークン
    • \ から始まる文字列 (\let, \relax 等).

なお, 制御トークンは, 展開されるトークンと展開されないトークン, \let で代入されたトークンに大別される.

展開されるトークン

トークンの種類
マクロ\def 等で定義されたトークン
条件分岐\if, \ifnum, \fi
展開制御\expandafter, \noexpand
組込みの変換\number, \string

展開されないトークン

トークンの種類
整数変数\newcount 等で定義されたトークン
整数演算\advance, \multiply
定義\def, \let
何もしない命令\relax
組版用の命令

整数

整数変数

整数変数は \newcount で定義する3.

ex01.tex
\input{stdio}

\newcount \Mynum
\Mynum = 10
\WriteStdout(\Mynum)
\WriteStdout(\number \Mynum)
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex01.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex01.tex (./stdio.tex)
\Mynum
10
)
warning  (pdf backend): no pages of output.
Transcript written on ex01.log.

上記の実行結果より, \Mynum は展開されないが, \number \Mynum は展開されていることが分かる.

整数リテラル

整数リテラルは, デフォルトで10進数, ' で8進数, " で16進数となる.

ex02.tex
\input{stdio}

\newcount \Mydec
\newcount \Myoct
\newcount \Myhex

\Mydec = 10
\Myoct = '12
\Myhex = "A

\WriteStdout(\Mydec is \number \Mydec.)
\WriteStdout(\Myoct is \number \Myoct.)
\WriteStdout(\Myhex is \number \Myhex.)
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex02.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex02.tex (./stdio.tex)
\Mydec is 10.
\Myoct is 10.
\Myhex is 10.
)
warning  (pdf backend): no pages of output.
Transcript written on ex02.log.

また, 整数リテラルの前後に展開されるトークンを以下のように置くと, 整数リテラルと展開されるトークンが (展開されて) 繋がってしまう.

ex03.tex
\input{stdio}

\newcount \MynumOne
\newcount \MynumTwo
\def \One{1}

\MynumOne = 12\One
\MynumTwo = \One12

\WriteStdout(\MynumOne is \number \MynumOne.)
\WriteStdout(\MynumTwo is \number \MynumTwo.)
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex03.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex03.tex (./stdio.tex)
\MynumOne is 121.
\MynumTwo is 112.
)
warning  (pdf backend): no pages of output.
Transcript written on ex03.log.

上記の実行結果より, まず, \One が展開され4, その後, 整数リテラルと繋がっていることが分かる.

これを防ぐには, 空白の文字トークンや \relax を入れる.

ex04.tex
\input{stdio}

\newcount \MynumOne
\newcount \MynumTwo
\def \One{1}

\MynumOne = 12 \One
\MynumTwo = 12\relax\One

\WriteStdout(\MynumOne is \number \MynumOne.)
\WriteStdout(\MynumTwo is \number \MynumTwo.)
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex04.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex04.tex (./stdio.tex)
\MynumOne is 12.
\MynumTwo is 12.
[1{c:/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map}])<c:/texlive/2
022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on ex04.pdf (1 page, 8173 bytes).
Transcript written on ex04.log.

なお, \Mynum = \One\relax12 とすると, \Mynum には 1 が代入される. これは, 制御トークンの後ろの空白は全て無視されるためである.

整数演算

整数演算では, 加算を \advance5, 乗算を \multiply, 除算を \divide で行える.

ex05.tex
\input{stdio}

\newcount \MynumOne
\newcount \MynumTwo

\MynumOne = 10
\MynumTwo = -5

\advance \MynumOne by 5
\WriteStdout(\MynumOne is \number \MynumOne)

\advance \MynumOne by \MynumTwo
\WriteStdout(\MynumOne is \number \MynumOne)

\multiply \MynumOne by 2
\WriteStdout(\MynumOne is \number \MynumOne)

\divide \MynumOne by \MynumTwo
\WriteStdout(\MynumOne is \number \MynumOne)
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex05.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex05.tex (./stdio.tex)
\MynumOne is 15
\MynumOne is 10
\MynumOne is 20
\MynumOne is -4
)
warning  (pdf backend): no pages of output.
Transcript written on ex05.log.

定数

定数は, \chardef で定義する.

ex06.tex
\input{stdio}

\chardef \One = 1

\WriteStdout(\One is \number \One.)
\end

実行結果は以下のようになる.

luatex --halt-on-error ex06.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex06.tex (./stdio.tex)
\One is 1.
)
warning  (pdf backend): no pages of output.
Transcript written on ex06.log.

条件分岐

条件分岐は \if...\fi または \if...\else...\fi で記述できる.

整数比較

整数の比較は \ifnum で行う.

ex07.tex
\input{stdio}

\newcount \Mynum
\Mynum = 1

\ifnum \Mynum > 0 \WriteStdout(Plus)\else \WtiteStdout(Minus)\fi
\ifnum \Mynum = 1 \WriteStdout(Ok)\fi
\ifnum 2 < \Mynum \WriteStdout(Over)\fi
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex07.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex07.tex (./stdio.tex)
Plus
Ok
)
warning  (pdf backend): no pages of output.
Transcript written on ex07.log.

上記の実行結果より, \Mynum は0より大きく, 1と等しいため PlusOk が展開され出力されていることが分かる. また, \Mynun は 2より小さいため, Over は展開されず出力されていないことも分かる.

トークン比較

トークンの比較をする制御トークンには \if\ifx がある. \if は展開後のトークンを比較し, \ifx はトークンの定義を比較する (展開しない).

ex08.tex
\input{stdio}

\def \MacroOne{a}
\def \MacroTwo{a}

\if a\MacroOne \WriteStdout(same)\else \WriteStdout(not same)\fi
\ifx a\MacroOne \WriteStdout(same)\else \WriteStdout(not same)\fi
\ifx \MacroOne\MacroTwo \WriteStdout(same)\else \WriteStdout(not same)\fi
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex08.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex08.tex (./stdio.tex)
same
not same
same
)
warning  (pdf backend): no pages of output.
Transcript written on ex08.log.

上記の実行結果より, a\MacroOneaa に展開されるため, same が出力されていることが分かる. また, a\MacroOne は (展開なしでは) 定義が一致しないため not same が出力され, \MacroOne\MacroTwo は定義が一致するため same が出力されていることが分かる.

これらを応用すると, 以下のような文字列比較マクロを定義できる.

ex09.tex
\input{stdio}

\def \IfSameString(#1,#2){
  \def \TmpMacroA{#1}
  \def \TmpMacroB{#2}
  \ifx \TmpMacroA\TmpMacroB
}

\IfSameString(abc,abc) \WriteStdout(same)\else \WriteStdout(not same)\fi
\IfSameString(abc,abd) \WriteStdout(same)\else \WriteStdout(not same)\fi
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex09.tex
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex09.tex (./stdio.tex)
same
not same
)
warning  (pdf backend): no pages of output.
Transcript written on ex09.log.

上記の実行結果より, 正常に文字列比較が行えていることが分かる.

新しい条件分岐の作成

新しい条件分岐の制御トークンは, \newif で作成できる. \newif \if<条件名> で新しい条件分岐を定義すると, \if<条件名> という条件分岐の制御トークンに加え, 条件を真にする \<条件名>true と条件を偽にする \<条件名>false という制御トークンが定義される.

ex10.tex
\input{stdio}

\newif \ifInRange

\def \CheckInRange(#1<=#2<=#3){
  \InRangetrue
  \ifnum #2 < #1 \InRangefalse \fi
  \ifnum #3 < #2 \InRangefalse \fi
}

\CheckInRange(0<=1<=2)
\ifInRange \WriteStdout(ok)\else \WriteStdout(out)\fi

\CheckInRange(-2<=1<=0)
\ifInRange \WriteStdout(ok)\else \WriteStdout(out)\fi
\end

実行結果は以下のようになる.

$ luatex --halt-on-error ex10.tex 
This is LuaTeX, Version 1.15.0 (TeX Live 2022) 
 restricted system commands enabled.
(./ex10.tex (./stdio.tex)
ok
out
)
warning  (pdf backend): no pages of output.
Transcript written on ex10.log.

上記の実行結果より, 正常に条件分岐が行えていることが分かる.

まとめ

今回は, TeX の基本について学び, 整数演算や条件分岐を行った. 次回6は, もう少し発展的な内容にも取り組みたい.

Footnotes

  1. 「いもあらい。」はサークル名.

  2. --halt-on-error オプションは, エラー発生時に処理系の実行を終了させるために指定している.

  3. TeX で 1+2 とすると 1, +, 2 というトークン列に分解される.

  4. 整数リテラルとして扱えるトークンがある可能性があるため.

  5. 減算は負数を加算することで実現する.

  6. 予定は未定🙃