-
Notifications
You must be signed in to change notification settings - Fork 0
/
Week8.tex
481 lines (411 loc) · 18.5 KB
/
Week8.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
\documentclass[autodetect-engine,dvipdfmx]{jsarticle}
\usepackage{okumacro}
\usepackage[top=25truemm,bottom=25truemm,left=25truemm,right=25truemm]{geometry}
\usepackage[dvipdfmx]{xcolor}
\usepackage{ascmac}
\usepackage{listings,jlisting}
\usepackage{latexltx2015}
\begin{document}
\title{ latex.ltxリーディング \\ 第8回資料 }
\author{ 東大\TeX 愛好会 }
\date{2015年11月9日}
\maketitle
\section{エラーメッセージまわりその1(大浦)}
\texttt{lterror.dtx}に由来する部分を読んでいく。長いので,すこしずつ。今日は定義の前半部を掲載するが,この全部を読み切ろうというわけでもない。すこしずつ,続けられるペースで読んでいきたいから...
\subsection{定義(Part 1)}
\latexltx
\begin{lstlisting}[firstnumber=877]
\expandafter\let\csname [email protected]\endcsname\fmtversion
\let\MessageBreak\relax
\DeclareRobustCommand{\GenericInfo}[2]{%
\begingroup
\def\MessageBreak{^^J#1}%
\set@display@protect
\immediate\write\m@ne{#2\on@line.}%
\endgroup
}
\DeclareRobustCommand{\GenericWarning}[2]{%
\begingroup
\def\MessageBreak{^^J#1}%
\set@display@protect
\immediate\write\@unused{^^J#2\on@line.^^J}%
\endgroup
}
\bgroup
\lccode`\@=`\ %
\lccode`\~=`\ %
\lccode`\}=`\ %
\lccode`\{=`\ %
\lccode`\T=`\T%
\lccode`\H=`\H%
\catcode`\ =11\relax%
\lowercase{%
\egroup%
\dimen@\ifx\@TeXversion\@undefined4\else\@TeXversion\fi\p@%
\ifdim\dimen@>3.14\p@%
\DeclareRobustCommand{\GenericError}[4]{%
\begingroup%
\immediate\write\@unused{}%
\def\MessageBreak{^^J}%
\set@display@protect%
\edef%
\@err@ %
{{#4}}%
\errhelp
\@err@ %
\let
\@err@ %
\@empty
\def\MessageBreak{^^J#1}%
\def~{\errmessage{%
#2.^^J^^J%
#3^^J%
Type H <return> for immediate help%
\@err@ %
}}%
~%
\endgroup}%
\else%
\DeclareRobustCommand{\GenericError}[4]{%
\begingroup%
\immediate\write\@unused{}%
\def\MessageBreak{^^J}%
\set@display@protect%
\edef%
\@err@ %
{{#4}}%
\errhelp
\@err@ %
\let
\@err@ %
\errmessage
\def\MessageBreak{^^J#1}%
\def~{\typeout{! %
#2.^^J^^J%
#3^^J%
Type H <return> for immediate help.}%
\@err@ %
{}}%
~%
\endgroup}%
\fi}%
\end{lstlisting}
\subsection{定義冒頭}
\latexltx
\begin{lstlisting}[firstnumber=877]
\expandafter\let\csname [email protected]\endcsname\fmtversion
\let\MessageBreak\relax
\end{lstlisting}
877行目は,\cmd{[email protected]}というコマンドを\cmd{fmtversion}の別名として定義しているだけである。
878行目は,あとで散々出てくる\cmd{MessageBreak}にとりあえず\cmd{relax}を代入して備えているだけである。あとから\cmd{MessageBreak}には色々なものが代入される。\cmd{MessageBreak}は,その名の通り,(エラー)メッセージ同士の区切りにどのようなものを使うかということである。
\subsection{GenericInfo}
\latexltx
\begin{lstlisting}[firstnumber=879]
\DeclareRobustCommand{\GenericInfo}[2]{%
\begingroup
\def\MessageBreak{^^J#1}%
\set@display@protect
\immediate\write\m@ne{#2\on@line.}%
\endgroup
}
\end{lstlisting}
ここで定義されるコマンドは,\cmd{GenericInfo}である。これを,引数を二つ取る堅牢なコマンドとして定義する。その中身はというと,さっき用意した\cmd{MessageBreak}として\preSub\verb|^^J#1|\preSub,つまり改行+第一引数を指定し,\cmd{set@display@protect}を実行し,すぐに\texttt{.log}ファイルに,\preSub\verb|#2\on@line.|\preSub を書くということである。\cmd{m@ne}は第7回資料にある通り,マイナス1のことであり,\cmd{write}の後に負の数が来た場合には\cmd{write}の第二引数は\texttt{.log}ファイル送りになるので,このような挙動を示す。
\cmd{set@display@protect}については,別途\texttt{latex.ltx}に定義がある。
\latexltx
\begin{lstlisting}[firstnumber=763]
\def\set@display@protect{\let\protect\string}
\end{lstlisting}
つまり,単に\cmd{protect}を,\cmd{string}の別名として定義する,という意味である。
\subsection{GenericWarning}
\latexltx
\begin{lstlisting}[firstnumber=879]
\DeclareRobustCommand{\GenericWarning}[2]{%
\begingroup
\def\MessageBreak{^^J#1}%
\set@display@protect
\immediate\write\@unused{^^J#2\on@line.^^J}%
\endgroup
}
\end{lstlisting}
先ほどとの違いは,2点ある。1つめはわかりやすいところだが,表示されるメッセージの内容が,\preSub\verb|^^J#2\on@line.^^J|\preSub となり,先ほどより前後の改行が多いということである。2つめは,\cmd{m@ne}が\cmd{@unused}になっているということである。
\cmd{@unused}は,\texttt{latex.ltx}の1339行目に次のようにして定義されている。
\latexltx
\begin{lstlisting}[firstnumber=879]
\newwrite\@unused
\end{lstlisting}
そして,\cmd{newwrite}は,第7回資料で述べられたとおり,\cmd{newcount}系のコマンドであり,次のように定義されている。
\latexltx
\begin{lstlisting}[firstnumber=332]
\def\newwrite{\alloc@7\write\chardef\sixt@@n}
\end{lstlisting}
また,ここに登場する\cmd{alloc@}は,次のように定義されているのであった。
\latexltx
\begin{lstlisting}[firstnumber=334]
\def\alloc@#1#2#3#4#5{\global\advance\count1#1\@ne
\ch@ck#1#4#2% make sure there's still room
\allocationnumber\count1#1%
\global#3#5\allocationnumber
\wlog{\string#5=\string#2\the\allocationnumber}}
\end{lstlisting}
そして,ここに登場する\cmd{ch@ck}は,次のように定義されていた。
\latexltx
\begin{lstlisting}[firstnumber=334]
\gdef\ch@ck#1#2#3{%
\ifnum\count1#1<#2\else
\errmessage{No room for a new #3}%
\fi}
\end{lstlisting}
では,
\texsource
\begin{lstlisting}
\newwrite\@unused
\end{lstlisting}
がどのように展開されていくのか追っていくことにしよう。
\noindent 第一段階
\texsource
\begin{lstlisting}
\alloc@7\write\chardef\sixt@@n\@unused
\end{lstlisting}
\noindent 第二段階
\texsource
\begin{lstlisting}
\alloc@7\write\chardef\sixt@@n\@unused
\end{lstlisting}
\noindent 第三段階
\texsource
\begin{lstlisting}
\global\advance\count17\@ne
\ch@ck7\sixt@@n\write
\allocationnumber\count17%
\global\chardef\@unused\allocationnumber
\wlog{\string\@unused=\string\write\the\allocationnumber}
\end{lstlisting}
\cmd{advance}が実行され,\cmd{count17}の値が1増える。\cmd{count17}の初期値は-1なので,ここで\cmd{count17}は0になることが多かろう。
\noindent 第四段階
\texsource
\begin{lstlisting}
\ifnum\count17<\sixt@@n\else
\errmessage{No room for a new \write}%
\fi
\allocationnumber\count17%
\global\chardef\@unused\allocationnumber
\wlog{\string\@unused=\string\write\the\allocationnumber}
\end{lstlisting}
ここの\cmd{ifnum}で,現在割り当てようとしているレジスタの番号が,16より小さいかを判別し,これを超過するようであればエラーが出る。このチェックを通り抜けた場合,\cmd{allocationnumber}に今から割り当てるレジスタの番号を格納し,
\texsource
\begin{lstlisting}
\global\chardef\@unused\allocationnumber
\end{lstlisting}
によって,レジスタに\cmd{@unused}を割り当てる。最後の\cmd{wlog}により,\texttt{.log}ファイルに\cmd{@unused}が定義されたことを書き込む。
この構成方法からわかるとおり,\cmd{@unused}に入っている値は,0以上15以下の数である。
\cmd{write}の第一引数として,負の数または15より大きい値が指定された時は\texttt{.log}ファイル送りになるので,\cmd{@unused}が上限値の15に達していた時でさえ\texttt{.log}ファイルに書き込まれ知らせてくれたりはしない。一方,\cmd{write}の第一引数として,0以上の数が指定された時にはターミナル送りになるので,今回は必ずターミナル送りとなる。そりゃユーザに対するWarningがターミナル送りにされなかったらそれは困るでしょう。
\subsection{GenericError}
InfoとWarningを見てきたが,次に大物のErrorである。Errorはタイプセットの流れを止めるわけだから,それこそ定義が大きくなるのは仕方がない話である。今日のところは,\cmd{GenericError}定義部のおおまかな構造を述べるにとどめ,詳細な定義は次回以降に述べることにする。定義を再掲しておくと,次のようになっている。本当に中かっこの数が一緒かどうか確かめるのでさえ一苦労である(当然ちゃんとあっているのだが)。
\latexltx
\begin{lstlisting}[firstnumber=893]
\bgroup
\lccode`\@=`\ %
\lccode`\~=`\ %
\lccode`\}=`\ %
\lccode`\{=`\ %
\lccode`\T=`\T%
\lccode`\H=`\H%
\catcode`\ =11\relax%
\lowercase{%
\egroup%
\dimen@\ifx\@TeXversion\@undefined4\else\@TeXversion\fi\p@%
\ifdim\dimen@>3.14\p@%
\DeclareRobustCommand{\GenericError}[4]{%
\begingroup%
\immediate\write\@unused{}%
\def\MessageBreak{^^J}%
\set@display@protect%
\edef%
\@err@ %
{{#4}}%
\errhelp
\@err@ %
\let
\@err@ %
\@empty
\def\MessageBreak{^^J#1}%
\def~{\errmessage{%
#2.^^J^^J%
#3^^J%
Type H <return> for immediate help%
\@err@ %
}}%
~%
\endgroup}%
\else%
\DeclareRobustCommand{\GenericError}[4]{%
\begingroup%
\immediate\write\@unused{}%
\def\MessageBreak{^^J}%
\set@display@protect%
\edef%
\@err@ %
{{#4}}%
\errhelp
\@err@ %
\let
\@err@ %
\errmessage
\def\MessageBreak{^^J#1}%
\def~{\typeout{! %
#2.^^J^^J%
#3^^J%
Type H <return> for immediate help.}%
\@err@ %
{}}%
~%
\endgroup}%
\fi}%
\end{lstlisting}
まず最初に,
\latexltx
\begin{lstlisting}[firstnumber=894]
\lccode`\@=`\ %
\lccode`\~=`\ %
\lccode`\}=`\ %
\lccode`\{=`\ %
\lccode`\T=`\T%
\lccode`\H=`\H%
\catcode`\ =11\relax%
\lowercase{%
\egroup%
\end{lstlisting}
でおなじみの(?)カテゴリコード変更である。
\cmd{lowercase}がでてくるあたりが\texttt{latex.ltx}らしい技巧ではなかろうか。
\latexltx
\begin{lstlisting}[firstnumber=903]
\dimen@\ifx\@TeXversion\@undefined4\else\@TeXversion\fi\p@%
\ifdim\dimen@>3.14\p@%
\end{lstlisting}
ここで,\TeX のバージョンが3.14より新しいかどうかを判別して,それによって\cmd{GenericError}の定義を変える条件分岐をしている。
そして,残りは927行目の\cmd{else}で区切られて,それぞれの条件におけるエラー生成コマンドを定義しているのである。
\section{\cmd{DeclareRobustCommand}の実装(大門)}
生来的に保護付(natively-protected)なマクロを定義するための制御綴である,\cmd{DeclareRobustCommand}の実装を考えていく。
\latexltx
\begin{lstlisting}[firstnumber=738]
\def\DeclareRobustCommand{\@star@or@long\declare@robustcommand}
\def\declare@robustcommand#1{%
\ifx#1\@undefined\else\ifx#1\relax\else
\@latex@info{Redefining \string#1}%
\fi\fi
\edef\reserved@a{\string#1}%
\def\reserved@b{#1}%
\edef\reserved@b{\expandafter\strip@prefix\meaning\reserved@b}%
\edef#1{%
\ifx\reserved@a\reserved@b
\noexpand\x@protect
\noexpand#1%
\fi
\noexpand\protect
\expandafter\noexpand\csname
\expandafter\@gobble\string#1 \endcsname
}%
\let\@ifdefinable\@rc@ifdefinable
\expandafter\new@command\csname
\expandafter\@gobble\string#1 \endcsname
}
\def\x@protect#1{%
\ifx\protect\@typeset@protect\else
\@x@protect#1%
\fi
}
\def\@x@protect#1\fi#2#3{%
\fi\protect#1%
}
\end{lstlisting}
\cmd{newcommand}または\cmd{renewcommand}の機能と共通する部分(第一引数の省略やアスタリスクの有無など)については,既にそれらの実装を調べた回の資料で示されているので,今回は制御綴の保護に関する部分を中心に考えていく。
\subsection{具体例での展開追跡}
具体例として,以下の\TeX ソースの展開・実行を追うことにする。
\texsource
\begin{lstlisting}
\DeclareRobustCommand{\ほげ}{ふが}
-> \@star@or@long\declare@robustcommand{\ほげ}{ふが}
(\@star@or@longの展開により,\let\l@ngrel@x\longが実行される)
-> \declare@robustcommand{\ほげ}{ふが}
-> \ifx#1\@undefined\else\ifx\ほげ\relax\else
\@latex@info{Redefining \string\ほげ}%
\fi\fi
\edef\reserved@a{\string\ほげ}%
\def\reserved@b{\ほげ}%
\edef\reserved@b{\expandafter\strip@prefix\meaning\reserved@b}%
\edef\ほげ{%
\ifx\reserved@a\reserved@b
\noexpand\x@protect
\noexpand\ほげ%
\fi
\noexpand\protect
\expandafter\noexpand\csname
\expandafter\@gobble\string\ほげ \endcsname
}%
\let\@ifdefinable\@rc@ifdefinable
\expandafter\new@command\csname
\expandafter\@gobble\string\ほげ \endcsname{ふが}
\end{lstlisting}
ここまでは簡単に展開されるので,この先の挙動を丁寧に考えていくことにする。
まず最初の3行の\cmd{ifx}による分岐では\cmd{ほげ}が定義済みかどうかを調べ,定義済みであればRedefiningする旨を報告する作業をしている。その先では,\cmd{reserved@a}と\cmd{reserved@b}にそれぞれ制御綴名とその置換えテキストを代入し,それらが一致するかどうかを比較している。
\subsubsection{\cmd{reserved@a}と\cmd{reserved@b}の比較について}
まず,\cmd{reserved@a}については,
\begin{lstlisting}
\edef\reserved@a{\string\ほげ}%
\end{lstlisting}
とあるように\preSub\verb|`\ほげ'|\preSub という制御綴名の文字列が代入される。対して\cmd{reserved@b}は,
\begin{lstlisting}
\def\reserved@b{\ほげ}%
\edef\reserved@b{\expandafter\strip@prefix\meaning\reserved@b}%
\end{lstlisting}
とあるように,一旦\cmd{ほげ}そのものに等値した後,2行目の\cmd{edef}の部分で\cmd{ほげ}の置換えテキストを代入している。ここを理解するにはまず\cmd{meaning}の挙動を理解せねばならないが,\cmd{meaning}とは引数にとる制御綴の意味を表す文字列に展開される\TeX プリミティブで,具体例として
\begin{lstlisting}
\def\Are#1{#1 is Are.}
\meaning\Are\par
\meaning\@undefined
\end{lstlisting}
を実行すれば,出力として
\begin{lstlisting}
macro:#1->#1 is Are.
undefined
\end{lstlisting}
が得られる。(特殊な設定をしない場合,\preSub\texttt{>}\preSub はフォントの問題で>と表示されると思われる。)
また,\cmd{strip@prefix}とは\texttt{latex.ltx}に以下のように定義されている制御綴である。
\begin{lstlisting}[firstnumber=129]
\def\strip@prefix#1>{}
\end{lstlisting}
これは\cmd{meaning}の展開結果である文字列のうち,\texttt{macro}から\texttt{->}までの部分を取り除く働きを持つので,結局のところ\cmd{reserved@b}には置換えテキスト(または\texttt{`undefined'})が代入されることになる。
これらが,\cmd{ほげ}のが\cmd{edef}で定義される部分の中の\cmd{ifx}で比較されることになる。
\begin{question}
\cmd{reserved@a}と\cmd{reserved@b}が一致するのは,どのような状況なのだろうか。言い換えると,
制御綴名とその置換えテキストが一致する場合とはどういう場合なのだろうか。
\end{question}
\subsubsection{\cmd{reserved@a}と\cmd{reserved@b}が一致しない場合}
とりあえず\cmd{ifx}が偽である場合を考える。特に,新しい制御綴を宣言する際には\cmd{reserved@b}に\texttt{`undefined'}が代入されるので,こちらの場合に相当する。このとき,
\begin{lstlisting}
\edef\ほげ{%
\ifx\reserved@a\reserved@b
\noexpand\x@protect
\noexpand\ほげ%
\fi
\noexpand\protect
\expandafter\noexpand\csname
\expandafter\@gobble\string\ほげ \endcsname
}%
\end{lstlisting}
の部分を(\cmd{edef}内であることに注意しながら)展開することにより,
\begin{lstlisting}
\ほげ <- \protect\ほげ
\end{lstlisting}
という代入操作が得られる。\cmd{protect}のあとに続く\cmd{ほげ }は\cmd{ほげ}とは別物であることに注意されたい。\cmd{ほげ }は\cmd{ほげ}の直後に半角空白が続いている制御綴である。ただしこの時点では\cmd{ほげ }(半角空白が直後に続く制御綴)は\cmd{relax}と等価な状態である(これは\cmd{csname}の作用による)。
続く以下の部分を再び示すと以下のようになる。
\begin{lstlisting}
\let\@ifdefinable\@rc@ifdefinable
\expandafter\new@command\csname
\expandafter\@gobble\string\ほげ \endcsname{ふが}
\end{lstlisting}
ここは至って単純で,\cmd{ほげ}の代わりに\cmd{ほげ }(半角空白が直後に続く制御綴)に対して,\cmd{newcommand}で行われた作業を実施しているだけである。ただし\cmd{DeclareRobustCommand}では既存の定義の書き換えも想定されているので,定義可能かどうかを調べる制御綴に\cmd{renewcommand}用の\cmd{@rc@ifdefinable}を割り当てている。この先の挙動は過去に扱ったので省略する。
結局のところ,\cmd{DeclareRobustCommand}は内部で半角空白が直後に続く制御綴\footnote{これは\cmd{def}などの普通の方法ではユーザーは定義できないので,衝突の可能性が低い。}を\cmd{newcommand}と同様に定義して,それに\cmd{protect}を被せる,という操作を行なっていると考えられる。
\subsubsection{\cmd{reserved@a}と\cmd{reserved@b}が一致する場合}
これについては次回考えることにする(時間ありませんでした…)。
\end{document}