目的 | 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.