マクロ研究室
LaTeX報告書生成マクロ・repgen

Last update on 2000.08.10

概要

目的 LaTeXコマンドをまったく入力することなしに、LaTeX文書を書けたらいいな
対象言語 pLaTeX2ε 2.1.8(たぶん)
記述言語 Awk

説明

 LaTeX(TeX)が超強力な組版言語でありプログラミング言語であること は、コンピューター 言語研究所の他のページで紹介した本を見てもらえればお判りかと 思います。

 超強力なのはうれしいのですが、(プログラミング)言語の弱点は、 したいことをするのにプログラムしなければならないというと ころです。これをある人は次のように言っています。「LaTeX(TeX)は確 かに強力だが、いちいちLaTeX(TeX)のソースを書くのは愚かである」。

 その頃、仕事場にはPC9801も極めて少なく、従って○太郎なんてワー ドプロセッサーもなく、親会社製のワードプロセッサー専用機はあった もののやはり台数も少なく、マッキントッシュはかろうじて一台か二台 といったありさまで、文書の整形・印刷にはとても厳しい状況でした。

 そんな中、MC68030を積んだUNIXワークステーションは元気で強力で、 一部の人たちはLaTeXを使って設計文書を作成していました。このパワー をみんなに解放してあげたい。そうすれば、この「ワープロ日照り」は ちょっとは解消されるだろう。

 しかし問題は、そのためにはみんなLaTeXを使えるようにならなけれ ばならない、ということでした。慣れてくれば自由自在に書けるし、雛 形を拵えておいて使い回すこともできるでしょうが、鬱陶しいものは鬱 陶しい。みんながみんなLaTeXに興味を持っているわけではないし、ま していちいち言語を学びたいわけじゃありません。(なんと、当時は ぼくも、LaTeXには対して興味のない人間でした)

 そこで、入力はできるだけ省いて、いわば「最小の労力でそこそこの LaTeX文書を生成する」ことができるようになればいいな。 と思って作ったのが、この「報告書生成マクロ」です。

 当時ぼくが入れ込んでいたAwkという言語を使って、マクロ処理系を 書きました。

動作環境

マクロ仕様

 マクロの仕様は、troffという植字言語に似せることにしました。 深い考えがあってのことではありません。ホントはこのマクロの仕様も 載せなければなりませんが、もうかなり昔の話で忘れているので、 ズルを決め込みます。 マクロ処理系のソースから想像してください(ひどいなあ)。

 それから、何分ずいぶん昔に書いたものなので、若書きです。 悪しからずご了承ください(永遠に若書きという話はさておき)。

 また、解説はちょっと勘弁してください。m(_ _)m

プログラム

 プログラムコードの各行の行頭についている行番号(": "まで)は参 考のためのものであり、実際のプログラムにはつきません。

  1: #! /usr/local/bin/jgawk -f
  2: #
  3: # repgen: 報告書作成マクロプロセッサ
  4: #
  5: #	Yukinoshin Saitoh
  6: #######################################################################
  7: #
  8: # 【概要】
  9: #	報告書などの(特に枠線のある)定型文書は
 10: #	LaTeXを使うときれいだけれど、
 11: #	LaTeXのコマンドをいちいち書き連ねるのはかったるいし、
 12: #	知らない人には望むべくもない。そこで、
 13: #	「簡単にLaTeXで報告書が書ける」ようにマクロを拵える。
 14: #	マクロはRoffのような感じになっている。
 15: #	また、この手法は報告書以外にも適用可能である。
 16: # 【方針】
 17: #	●「指令」を一切含まない文書が与えられても、
 18: #	 筋の通った出力を吐くこと。
 19: #	●日本語LaTeX(jlatex)を利用する。
 20: #	●スタイルファイルはすでに誰かが創ってくれた
 21: #	 報告書用のものを利用することにする。
 22: #	 創ってくれた人に、感謝。
 23: #	●おきらくごくらく。(……)
 24: #
 25: # 【注意】
 26: # ・このマクロプロセッサで吐き出されるTeXコマンドは、
 27: #  スタイルファイルに依存する部分もある(のだと思う)
 28: #
 29: # 【TeX特殊文字のクォート】
 30: #	repgenは、利用者がLaTeXを知らないものと決めつける。
 31: #	そのままLaTeXに送り込むと、特殊文字やコマンドと解釈される
 32: #	危険のある文字(列)を(LaTeXのために)クォートする。
 33: #	逆に、LaTeXコマンドを書く場合は、そのコマンドシークエンス全体を
 34: #	予めrepgenとしてクォートしておく必要がある。(複雑だね)
 35: #	クォートには '(...)という由緒正しい表記を用いる:-)
 36: #
 37: # 【仕様】
 38: #	三つの<モード>と、後述するマクロから構成される。
 39: #	○主モード……ごく普通のモード
 40: #	○字下げモード……インデントする。
 41: #		行頭のタブは、“字下げのモード”と見なされる。
 42: #		この状態では、行揃えはなされない。
 43: #		空白行か、行頭にコマンドでない文字を含む行が
 44: #		現れたときに終わる。
 45: #	○箇条書モード……箇条書する。
 46: #		「・」で始まる行が現れると、“箇条書モード”になる。
 47: #		行頭にタブを0個以上含み、
 48: #		「・」か全角スペースで始まる行が続く間続く。
 49: #		行頭がタブでも「・」でも全角スペースでもない文字を
 50: #		含む行が現れたときに終わる。
 51: #
 52: # 【マクロ】
 53: # マクロは行頭に書くこと。また、引数とは空白文字で区切られる。
 54: # <>は必須引数を、[]は省略可能な引数を示す
 55: # ●ヘッダ:これらは本文より前に現れていなければならない
 56: #	.ti (.title) <標題>	報告書の標題。ない場合は空白
 57: #	.da (.date) [日付]	発行日。ない場合はその日の日付
 58: #	.au (.author) <作成者>	作成者名。ない場合は空白
 59: # ●文書の構成
 60: #	.se (.section) [題]	大見出し
 61: #	.ss (.subsection) [題]	小見出し
 62: # ●段落、ページの制御
 63: #	.pp (.paragraph) [行数]
 64: #			新しい段落(字下げが起こる)
 65: #			行数を指定すると、その分空けて段落を始める
 66: #	.nl (.newline) [行数]
 67: #			改行。段落は変わらない。行数の意味は段落と同じ
 68: #	.np (.newpage)	改ページ
 69: # ●“字下げモード”
 70: #	行頭のタブ	“字下げモード”に入る
 71: # ●書体、文字サイズ、修飾:これらはグループ化される
 72: #	.bo (.bold)	強調(太字)
 73: #	.go (.gothic)	ゴシック体
 74: #	.la (.large)	大きい字(\Large)。書体は既定値に戻る
 75: #	.ul (.uline)	下線
 76: # ●作表
 77: #	.ta (.table) [欄の幅,...]
 78: #			表を作る。.zzで終わる
 79: #			引数に、欄の数分、欄幅を書く
 80: # ●箇条書
 81: #	行頭の・	“箇条書モード”に入る
 82: # ●“あるがまま”モード
 83: #	.as (.asis)	半角の書体をモノスペースにし、
 84: #			タブをスペースに展開する。.zzで終わる
 85: #			キャラクタで図が書ける。
 86: #			ソースリストや疑似コードも書ける。
 87: #			タイプライタ体の幅≠明朝の文字幅/2 なので、
 88: #			半角と全角を混ぜるとおかしな具合になる. 
 89: #			日本語は、行の終わりに寄せるようにして解決
 90: #			ホントは、figureをマクロにするべきなんだろうな……
 91: # ●コメント
 92: #	.;; (.comment)	コメント。その行をLaTeXのコメントとして出力。
 93: # ●その他
 94: #	.zz 		.go, .ul, .la, .taの終わりの目印
 95: #	"以上"だけの行	右寄せで出力される。(やりすぎ)
 96: #
 97: # 【バグ】
 98: #	“字下げモード”、作表の中では、マクロが使えない……
 99: #	マクロのエラー処理はどうしよう
100: #
101: # 【Globals】
102: #	gVersion	バージョン番号
103: #	gTextProcessed	テキストを処理したかどうか
104: #	gIndentDepth	字下げの深さ
105: #	gInTexQuote	TeXコマンドのクォート処理
106: #
107: 
108: #
109: # 初期の設定と出力. 何かしら決めておかないといけないデータを設定する
110: #
111: BEGIN{
112: 	gVersion = "1.01"
113: 	gTextProcessed = -1
114: 	if(ARGC == 1){
115: 		printf("repgen: version %s\n", gVersion);
116: 		exit;
117: 		# このexitは、けっこう危険なトリック. 
118: 		# Awkでは、END以外の場所でexitを実行すると、
119: 		# (あれば)ENDアクションが実行される. 
120: 		# そこで、「テキストを処理したよ」変数を用意して
121: 		# ENDアクションを制御する...
122: 	}
123: 	gTextProcessed = 0
124: 
125: 	FS = "[ \t]+"	# 欄はひとつ以上の空白またはタブで区切られる
126: 	date = "\\today"	# 日付の既定値は、今日の日付
127: 	title = "\\ "	# 標題の既定値は、空白(TeXコマンド)
128: 	author = ""	# 作成者の既定値は、なし
129: 
130: 	gIndentDepth = 0
131: 	gInTexQuote = 0
132: 
133: 	# TeX文書の最初の呪文
134: 	# [ ]の中はスタイルファイルのオプションで、場合によって異なる
135: 	printf("\\documentstyle{jarticle}\n");
136: 	printf("\\begin{document}\n");
137: 	printf("\n");
138: 	# 初期値の出力
139: 	printf("\\date{%s}\n", date);
140: 	printf("\\title{%s}\n", title);
141: 	printf("\\author{%s}\n", author);
142: 	# インデント定義. これはスタイルファイルに書いておきたい……
143: 	print "";
144: 	printf("\\newenvironment{indentation}[1]{\\par\n");
145: 	printf("\\addtolength{\\leftskip}{#1}\n");
146: 	printf("\\begingroup}{\\endgroup\\par}\n");
147: 	print "";
148: 	## printf("\\tt\n"); # 欧文書体をタイプライタ体にする(不要)
149: }
150: 
151: #######################################################################
152: # 主モードのアクション. すべての入力行はこのアクションを通過する
153: {
154: 	if($0 ~ /^(\t)/){
155: 		new_indent_mode($0);
156: 	}
157: 	else if($0 ~ /^・/){
158: 		itemize_mode($0);
159: 	}
160: 	else if($0 ~ /^以上$/){
161: 		allover($0);
162: 	}
163: 	else if($0 ~ /^\./){	# it seems to be some macro...
164: 		evalcommand($0);
165: 	}
166: 	else if($0 == ""){
167: 		print "";
168: 	}
169: 	else{
170: 		normaltext($0);		# 指令行でない、普通の文書行
171: 	}
172: }
173: 
174: # 文書行を印字する
175: function normaltext(text,    tmpdepth)
176: {
177: 	if(gIndentDepth > 0){	# something wrong
178: 		resetindent(0);
179: 	}
180: 	printtext(evaltext(text));
181: }
182: 
183: function printtext(text)
184: {
185: 	print text;
186: }
187: 
188: # 行頭に「.」があるものをrepgenマクロとして評価する
189: # マクロでないものはそのまま透過される
190: function evalcommand(text)
191: {
192: 	# $1を比較に持ち出すのは危険. textがよろしい(筈)
193: 	# textを配列に分解すればよろしい
194: 	if($1 ~ /^(\.da)|(\.date)/) {
195: 		macro_date(text);
196: 	}
197: 	else if($1 ~ /^(\.ti)|(\.title)/) {
198: 		macro_title(text);
199: 	}
200: 	else if($1 ~ /^(\.au)|(\.author)/) {
201: 		macro_author(text);
202: 	}
203: 	else if($1 ~ /^(\.se)|(\.section)/) {
204: 		macro_section(text);
205: 	}
206: 	else if($1 ~ /^(\.ss)|(\.subsection)/) {
207: 		macro_subsec(text);
208: 	}
209: 	else if($1 ~ /^(\.nl)|(\.newline)/) {
210: 		macro_newline(text);
211: 	}
212: 	else if($1 ~ /^(\.pp)|(\.paragraph)/) {
213: 		macro_paragraph(text);
214: 	}
215: 	else if($1 ~ /^(\.np)|(\.newpage)/) {
216: 		macro_newpage(text);
217: 	}
218: 	else if($1 ~ /^(\.go)|(\.gothic)/) {
219: 		macro_gothic(text);
220: 	}
221: 	else if($1 ~ /^(\.bo)|(\.bold)/) {
222: 		macro_bold(text);
223: 	}
224: 	else if($1 ~ /^(\.la)|(\.large)/) {
225: 		macro_large(text);
226: 	}
227: 	else if($1 ~ /^(\.ul)|(\.uline)/) {
228: 		macro_uline(text);
229: 	}
230: 	else if($1 ~ /^(\.ta)|(\.table)/) {
231: 		macro_table(text);
232: 	}
233: 	else if($1 ~ /^(\.as)|(\.asis)/) {
234: 		macro_asis(text);
235: 	}
236: # 次の三行を追加したらjgawkのSyntax Errorで
237: # yacc stack overflowと言われた. yaccの限界……?
238: # 今はもう使わないからいいけど
239: #	else if($1 ~ /^(\.so)|(\.source)/) {
240: #		macro_draft(text);
241: #	}
242: 	else if($1 ~ /^(\.;;)|(\.comment)/) {
243: 		macro_comment(text);
244: 	}
245: 	else if($1 ~ /^(\.zz)/) {
246: 		macro_zz(text);
247: 	}
248: 	else{
249: 		printtext(evaltext(text));
250: 	}
251: }
252: 
253: # 日付
254: function macro_date(text)
255: {
256: 	# 関数の中で$2とか参照しておかしくならない?
257: 	if(NF > 1 && $2 != ""){
258: 		date = $2;
259: 	}
260: 	else{
261: 		date = "\\today";	# TeX command
262: 	}
263: 	printf("\\date{%s}\n", date);
264: }
265: 
266: # 標題. 重複してもよいらしい(TeXは気にしないようだ)ので、気にせずやる
267: function macro_title(text)
268: {
269: 	if(NF > 1){
270: 		sub(/^(\.ti)|(\.title)[ \t]+/, "", text)
271: 		title = evaltext(text);
272: 	}
273: 	for(getline; $0 ~ /^\t/; getline){
274: 		sub(/^\t+/, "");
275: 		title = title "\\\\" evaltext($0);
276: 	}
277: 	printf("\\title{{\\bf %s}}\n", title); # 強調体で書いてあげよう
278: 	# 先読みしている
279: 	if(ismacro($0)){
280: 		evalcommand($0);
281: 	}
282: }
283: 
284: # 作成者
285: function macro_author(text)
286: {
287: 	if(NF > 1){
288: 		author = evaltext($2);
289: 	}
290: 	printf("\\author{%s}\n", author);
291: }
292: 
293: # 節
294: function macro_section(text,    section)
295: {
296: 	if(gIndentDepth > 0){
297: 		resetindent(0);
298: 	}
299: 	if(NF > 1){
300: 		sub(/^(\.se)|(\.section)[ \t]+/, "", text)
301: 		section = evaltext(text);
302: 	}
303: 	else{
304: 		section = "";
305: 	}
306: 	printf("\\section{%s}\n", section);
307: }
308: 
309: # 小節
310: function macro_subsec(text,    subsection)
311: {
312: 	if(gIndentDepth > 0){
313: 		resetindent(0);
314: 	}
315: 	if(NF > 1){
316: 		sub(/^(\.ss)|(\.subsection)[ \t]+/, "", text)
317: 		subsection = evaltext(text);
318: 	}
319: 	else{
320: 		subsection = "";
321: 	}
322: 	print "";
323: 	printf("\\subsection{%s}\n", subsection);
324: }
325: 
326: # 改行
327: function macro_newline(text,    nlines)
328: {
329: 	if(NF > 1 && $2 ~ /^[1-9][0-9]*$/){
330: 		nlines = $2;
331: 	}
332: 	else{
333: 		nlines = 1;
334: 	}
335: 	for(; nlines > 0; nlines--){
336: 		printf("\\ \\\\ \n");	# TeX command
337: 	}
338: }
339: 
340: # 段落
341: function macro_paragraph(text,    nlines)
342: {
343: 	if(gIndentDepth > 0){
344: 		resetindent(0);
345: 	}
346: 	if(NF > 1 && $2 ~ /^[1-9][0-9]*$/){
347: 		nlines = $2;
348: 	}
349: 	else{
350: 		nlines = 0;
351: 	}
352: 	for(; nlines > 0; nlines--){
353: 		printf("\\ \\\\ \n");	# TeX command
354: 	}
355: 	print "";	# TeX command; new paragraph
356: }
357: 
358: # 改ページ
359: function macro_newpage(text)
360: {
361: 	if(gIndentDepth > 0){
362: 		resetindent(0);
363: 	}
364: 	print "";
365: 	printf("\\clearpage\n");	# TeX command
366: 	print "";
367: }
368: 
369: # ゴシック体
370: function macro_gothic(target)
371: {
372: 	if(NF > 1){
373: 		sub(/^(\.go)|(\.gothic)[ \t]+/, "", target)
374: 		printf("{\\gt %s}\n", evaltext(target));
375: 	}
376: 	else{
377: 		printf("{\\gt\n");
378: 	}
379: }
380: 
381: # 強調体
382: function macro_bold(target)
383: {
384: 	if(NF > 1){
385: 		sub(/^(\.bo)|(\.bold)[ \t]+/, "", target)
386: 		printf("{\\bf %s}\n", evaltext(target));
387: 	}
388: 	else{
389: 		printf("{\\bf\n");
390: 	}
391: }
392: 
393: # 大きな字
394: function macro_large(target)
395: {
396: 	if(NF > 1){
397: 		sub(/^(\.la)|(\.large)[ \t]+/, "", target)
398: 		#printf("{\\Large\\tt %s}\n", target);
399: 		printf("{\\Large %s}\n", evaltext(target));
400: 	}
401: 	else{
402: 		#printf("{\\Large\\tt\n");
403: 		printf("{\\Large\n");
404: 	}
405: }
406: 
407: # 下線
408: function macro_uline(target)
409: {
410: 	if(NF > 1){
411: 		sub(/^(\.ul)|(\.uline)[ \t]+/, "", target)
412: 		printf("\\underline{%s}\n", evaltext(target));
413: 	}
414: 	else{
415: 		printf("\\underline{\n");
416: 	}
417: }
418: 
419: # その行をTeXのコメントとして出力. ここではクォート処理は行なわない
420: function macro_comment(text)
421: {
422: 	sub(/^(\.;;)|(\.comment)/, "", text)
423: 	printf("%%%s\n", text);
424: 	#for(ret = getline; ret > 0 && $1 != ".zz"; ret = getline){
425: 	#	sub(/^(\.;;)|(\.comment)/, "", $0)
426: 	#	printf("%%%s\n", $0);
427: 	#}
428: }
429: 
430: # マクロの作用対象終わり
431: function macro_zz(text)
432: {
433: 	printf("}\n");
434: }
435: 
436: #作表. これはむしろモードと呼ぶべきだね
437: function macro_table(text,    tabular, ret, i, savedFS)
438: {
439: 	if(NF < 2){
440: 		tabular = "\\begin{tabular}{p{4cm}p{4cm}p{4cm}}";
441: 	}
442: 	else{
443: 		tabular = "\\begin{tabular}{";
444: 		for(i = 2; i <= NF; i++){
445: 			tabular = tabular sprintf("p{%dcm}", $i);
446: 		}
447: 		tabular = tabular "}";
448: 	}
449: 	printf("\\begin{center}\n");
450: 	printf("%s \\hline\n", tabular);
451: 	savedFS = FS;
452: 	FS = "\t+";
453: 	# .zzが現れるまで、テキストを吐き出す
454: 	# これのおかげで、コマンドを埋め込めない
455: 	for(ret = getline; ret > 0 && $1 != ".zz"; ret = getline){
456: 		#if($1 ~ /^\./){
457: 		#	evalcommand($0);
458: 		#}
459: 		#else
460: 		      if($1 == ""){
461: 			i = 2;
462: 		}
463: 		else{
464: 			i = 1;
465: 		}
466: 		text = "";
467: 		for(; i <= NF; i++){
468: 			# クォートした上で、連結
469: 			text = text evaltext($i) " & ";
470: 		}
471: 		sub(/ \& $/, "\\\\", text);	# 最後の&を\\に
472: 		printtext(text);
473: 	}
474: 	printf(" \\hline\n");
475: 	printf("\\end{tabular}\n");
476: 	printf("\\end{center}\n\n");
477: 	FS = savedFS;
478: }
479: 
480: #テキストを“あるがまま”に扱う. これもモードだね
481: function macro_asis(text,    tabular)
482: {
483: 	printf("\\begin{verbatim}\n");
484: 	# .zzが現れるまで、テキストを吐き出す
485: 	for(ret = getline; ret > 0 && $1 != ".zz"; ret = getline){
486: 		# タブを空白に変換して出力
487: 		text = tab2spc($0, 8);
488: 		printtext(text);
489: 	}
490: 	printf("\\end{verbatim}\n");
491: }
492: 
493: # 一行まるごとタブをスペースに変換
494: function tab2spc(inText, tabWidth,    outText, len, iPos, oPos, spcNum)
495: {
496: 	outText = "";
497: 	oPos = 1;
498: 	len = length(inText);
499: 	for(iPos = 1; iPos <= len; iPos++){
500: 		if((c = jsubstr(inText, iPos, 1)) == "\t"){
501: 			spcNum = tabWidth - (oPos % tabWidth) + 1;
502: 			while(spcNum--){
503: 				outText = outText " ";
504: 				oPos++;
505: 			}
506: 		}
507: 		else{
508: 			outText = outText c;
509: 			oPos += length(c);
510: 		}
511: 	}
512: 	return outText;
513: }
514: 
515: #######################################################################
516: function new_indent_mode(text)
517: {
518: 	printf("\\begin{verbatim}\n");
519: 	printtext(tab2spc(text, 8));
520: 	# 行頭にタブでもコマンドでもない文字が現れたときに終わる
521: 	for(ret = getline; ret > 0 && $0 ~ /^(\t)/; ret = getline){
522: 		# タブを空白に変換して出力
523: 		text = tab2spc($0, 8);
524: 		printtext(text);
525: 	}
526: 	printf("\\end{verbatim}\n");
527: 	# 一行先読みしている. 
528: 	if(ismacro($0)){
529: 		evalcommand($0);
530: 	}
531: 	else{
532: 		normaltext($0);
533: 	}
534: }
535: 
536: # “字下げ”モード
537: function indent_mode(text,    tmpdepth)
538: {
539: 	# タブの個数を数える。一個がひとつのインデントに相当……
540: 	# ここには主モードからしか来ない。最初は必ず字下げが起こる
541: 	# だから、字下げの深さは局所変数でよい
542: 	tmpdepth = countdepth(text);
543: 	setindent(tmpdepth);
544: 	gIndentDepth = tmpdepth;
545: 	#sub(/^\t+/, "", text);
546: 	printf("%s\n", text);
547: 	# 行頭にタブでもコマンドでもない文字が現れたときに終わる
548: 	for(ret = getline; ret > 0 && $0 ~ /^(\t)/; ret = getline){
549: 		tmpdepth = countdepth($0);
550: 		if(tmpdepth == gIndentDepth){
551: 			printf("\\ \\\\\n");
552: 		}
553: 		else if(tmpdepth > gIndentDepth){
554: 			setindent(tmpdepth);
555: 			gIndentDepth = tmpdepth;
556: 		}
557: 		else if(tmpdepth < gIndentDepth){
558: 			resetindent(tmpdepth);
559: 			gIndentDepth = tmpdepth;
560: 		}
561: 		#sub(/^\t+/, "", $0);
562: 		printf("%s\n", evaltext($0)); # クォートする
563: 	}
564: 	# 一行先読みしている. 
565: 	if(ismacro($0)){
566: 		evalcommand($0);
567: 	}
568: 	else{
569: 		normaltext($0);
570: 	}
571: }
572: 
573: # 字下げをする. 【副作用】gIndentDepthがtmpdepthに等しくなる
574: function setindent(tmpdepth)
575: {
576: 	for(; gIndentDepth < tmpdepth; gIndentDepth++){
577: 		printf("\\begin{indentation}{40pt}\n");
578: 		printf("\\noindent\n");
579: 	}
580: }
581: 
582: # 字下げを取り止める. 【副作用】gIndentDepthがtmpdepthに等しくなる
583: function resetindent(tmpdepth)
584: {
585: 	for(; gIndentDepth > tmpdepth; gIndentDepth--){
586: 		printf("\\end{indentation}\n");
587: 		printf("\\noindent\n");
588: 	}
589: }
590: 
591: # 先頭のタブの個数(字下げの深さ)を数える
592: function countdepth(text,    len, count, n)
593: {
594: 	len = length(text);
595: 	count = 0;
596: 	for(n = 1; n < len; n++){
597: 		# 93.07.30 debug. タブでないものが現れたらおしまい
598: 		if(substr(text, n, 1) != "\t"){
599: 			break;
600: 		}
601: 		count++;
602: 	} 
603: 	return count;
604: }
605: 
606: #######################################################################
607: #箇条書モード
608: function itemize_mode(text,    itemdepth, depth, ret)
609: {
610: 	itemdepth = 0;
611: 	printf("\\begin{itemize}\n");
612: 	# この先、再帰プロセス
613: 	sub(/^・/, "", text);
614: 	printtext(sprintf("\\item\t%s", text));
615: 	# タブか「・」か「 」が続く間、、テキストを吐き出す
616: 	# この仕様のおかげで、コマンドを埋め込むことができる……
617: 	ret = getline;
618: 	for(; ret > 0 && $0 ~ /^(\t)|・| |(\.)/; ret = getline){
619: 		if(ismacro($0)){
620: 			evalcommand($0);
621: 			continue;
622: 		}
623: 		depth = countdepth($0);
624: 		if(itemdepth < depth && $0 ~ /^\t*・/){
625: 			printf("\\begin{itemize}\n");
626: 			# 自分を呼ぶ
627: 			itemdepth++;
628: 		}
629: 		else if(itemdepth > depth && $0 ~ /^\t*・/){
630: 			for(; itemdepth > depth; itemdepth--){
631: 				printf("\\end{itemize}\n");
632: 			}
633: 		}
634: 		# タブを取り去り、先頭文字を見て決める
635: 		sub(/^\t+/, "", $0);
636: 		if($0 ~ /^・/){
637: 			sub(/^・/, "", $0);
638: 			text = $0;
639: 			printtext(sprintf("\\item\t%s", evaltext(text)));
640: 		}
641: 		else{	# if($0 ~ /^ /)
642: 			sub(/^ /, "", $0);
643: 			text = $0;
644: 			printtext(sprintf("\t%s", evaltext(text)));
645: 		}
646: 	}
647: 	for(; itemdepth > 0; itemdepth--){
648: 		printf("\\end{itemize}\n");
649: 	}
650: 	printf("\\end{itemize}\n\n");
651: 	# 一行先読みしている. が、それがマクロである可能性はない
652: 	if(ismacro($0)){
653: 		evalcommand($0);
654: 	}
655: 	else{
656: 		normaltext($0);
657: 	}
658: }
659: 
660: #######################################################################
661: # テキストに現れるTeXコマンド、メタキャラクタをクォートする
662: # また、クォートされた文字列を評価しないようにする
663: function evaltext(text,    otext, len, n, c)
664: {
665: 	# メタキャラクタは以下のとおり
666: 	# #	コマンドや環境の引数		 -> \#
667: 	# $	数式モードと他のモードの区切り	 -> \$
668: 	# &	表の要素の区切り		 -> \&
669: 	# ~	改行しない単語間スペース	 -> \tt\symbol{"7e}
670: 	# _	数式モードでの下つき添え字	 -> \_
671: 	# ^	数式モードでの上つき添え字	 -> \tt\symbol{"5e}
672: 	# \	コマンドの先頭			 -> \tt\symbol{"5c}
673: 	# %	コメントの開始			 -> \%
674: 	# {	グループ化または引数の開始	 -> \{
675: 	# }	グループ化または引数の終了	 -> \}
676: 	#
677: 	# ほかに、次の文字も危ない
678: 	# <	さかさまの!(\tt\symbol{"3c}でクォート……)
679: 	# >	さかさまの?(\tt\symbol{"3e}でクォート……)
680: 	#
681: 	# また、'(..maybe TeX command..)という文字列を見つけると、
682: 	# 上のような評価をせず、そのまま送り出す
683: 
684: 	len = length(text);
685: 	otext = "";
686: 	for(n = 1; n <= len; n++){
687: 		c = jsubstr(text, n, 1);
688: 		if(gInTexQuote){
689: 			if(c == "("){
690: 				gInTexQuote++;
691: 			}
692: 			else if(c == ")"){
693: 				gInTexQuote--;
694: 				if(gInTexQuote == 0){
695: 					c = "";	# 閉じ括弧を取り除く
696: 				}
697: 			}
698: 			otext = otext c;
699: 		}
700: 		else if(c == "'" && jsubstr(text, n+1, 1) == "("){
701: 			# クォート記号の2バイトを取り除く
702: 			n ++;
703: 			gInTexQuote = 1;
704: 		}
705: 		else{
706: 			otext = otext quote(c);
707: 		}
708: 	}
709: 
710: 	return otext;
711: }
712: 
713: # 文字をクォートする. クォートしない
714: function quote(c,    q)
715: {
716: 	if(c == "#"){
717: 		q = "\\#";
718: 	}
719: 	else if(c == "$"){
720: 		q = "\\$";
721: 	}
722: 	else if(c == "%"){
723: 		q = "\\%";
724: 	}
725: 	else if(c == "^"){
726: 		q = "{\\tt \\symbol{94}}";
727: 	}
728: 	else if(c == "&"){
729: 		q = "\\&";
730: 	}
731: 	else if(c == "_"){
732: 		q = "\\_";
733: 	}
734: 	else if(c == "~"){
735: 		q = "{\\tt \\symbol{112}}";
736: 	}
737: 	else if(c == "\\"){
738: 		q = "{\\tt \\symbol{92}}";
739: 	}
740: 	else if(c == "{"){
741: 		q = "\\{";
742: 	}
743: 	else if(c == "}"){
744: 		q = "\\}";
745: 	}
746: 	else if(c == "<"){
747: 		q = "{\\tt \\symbol{60}}";
748: 	}
749: 	else if(c == ">"){
750: 		q = "{\\tt \\symbol{62}}";
751: 	}
752: 	else{
753: 		q = c;
754: 	}
755: 	return q;
756: }
757: 
758: # 行頭に現れる「以上」を右寄せで印刷させる
759: function allover(text)
760: {
761: 	print "";
762: 	printf("\\begin{flushright}\n");
763: 	print text;
764: 	printf("\\end{flushright}\n");
765: }
766: 
767: # マクロかどうか?
768: function ismacro(text)
769: {
770: 	if(text ~ /^\./){
771: 		return 1;
772: 	}
773: 	return 0;
774: }
775: 
776: # 後始末
777: END{
778: 	if(gTextProcessed == -1){
779: 		exit;
780: 	}
781: 	# もし字下げがされたままだったら、直す
782: 	if(gIndentDepth > 0){
783: 		print "";
784: 		resetindent(0);
785: 	}
786: 	# TeX文書の最後の呪文
787: 	print "";
788: 	printf("\\end{document}\n");
789: }
790: 
791: # end of repgen

(2000.08.10)

マクロ研究室 目次へ
(C) ©Copyright Noboru HIWAMATA (nulpleno). All rights reserved.