gokuro 言語について

gokuro はこのサイト (https://inzkyk.xyz/) のために作った簡単なマクロ言語です。このサイトおよび BOOTH にある PDF は全て gokuro を使って書かれています。仕様が固まったので公開します。

$ cat greeting.org
#+MACRO: greeting hello $1!
<<<greeting(gokuro)>>>

$ gokuro < greeting.org
hello gokuro!

とりあえず試したい方はデモページをどうぞ。GitHub リポジトリには C, Go, Node.js の実装があります。このページでは gokuro の仕様や設計を説明します。

gokuro の特徴

  1. 機能が最小限

    gokuro が持つ特別な構文はマクロの定義と呼び出しだけで、非常に簡単です。

  2. 仕様が固まっている

    gokuro の仕様はこのページで全て解説されており、今後変更する予定はありません。

  3. 導入が容易

    特別な構文を持たない文字列を gokuro に入力すると、入力がそのまま出力されます。そのため入力フォーマットには好きなものを使うことができ、途中まで書かれた文書に gokuro を導入することも可能です。

  4. 複数のフォーマットに出力できる

    gokuro を使うと、HTML と LaTeX といった複数のフォーマットに変換できる文書を書くことができます。

  5. 出力のカスタマイズが容易

    gokuro は組み込みのマクロを持たず、全てのマクロを入力ファイル内で定義します。そのため出力の調整が簡単に行えます。またコマンドラインから与えるオプションも存在しないので、著者が出力を完全に管理できます。

  6. 実装が簡単

    gokuro の機能や文法は実装が簡単になるように選んでいます。標準ライブラリの充実した Go や JavaScript なら 200 行程度で実装でき、C で完全にメモリを管理したとしても 700 行程度で実装できます。

  7. 実装が高速

    gokuro の機能や文法は実装が高速になるようにも選んでいます。gokuro の処理はワンパスであり、メインループで一切メモリをアロケートしないように実装することも可能です。

    C 実装を使うと、純粋数学講義 の原稿ファイル (64000 行, 2.75 MB) から 40 ミリ秒程度で tex ファイルを出力できます。参考までに示しておくと、同じファイルを pandoc で tex ファイルに変換すると 7.5 秒程度 (180 倍以上) かかります。pandoc --version でさえ 40 ミリ秒より長くかかります。

  8. 使用実績がある

    gokuro はこのサイトの全てのページと BOOTH で販売されている PDF の作成に使われています。

使う

gokuro の完全な JavaScript 実装と簡単な解説がデモページにあります。

GitHub リポジトリには C, Go, Node.js の実装があります。実用する場合には高速な C 実装を使ってください。

仕様

gokuro の仕様を例と共に説明します。gokuro という名前の gokuro 処理系にパスが通っているとします。

特別な構文が含まれない入力はそのまま出力されます:

$ cat input.org
hello world!
$ gokuro < input.org
hello world!

#+MACRO: NAME BODY という行は名前が NAME で本体が BODY のマクロを定義します。マクロを定義する行は出力されません:

$ cat input.org
#+MACRO: value 42
hello world!
$ gokuro < input.org
hello world!

定義したマクロは <<<NAME>>> で呼び出します。マクロの呼び出しが本体で置き換わります:

$ cat input.org
#+MACRO: value 42
the value is <<<value>>>.
$ gokuro < input.org
the value is 42.

マクロには引数を渡すことができます。マクロの呼び出しを <<<NAME(ARG1,ARG2,...)>>> とすると、マクロの本体に含まれる $1, $2, ... がそれぞれ ARG1, ARG2, ... で置き換わります:

$ cat input.org
#+MACRO: tag <$1>$2</$1>
<<<tag(strong,hello world!)>>>
$ gokuro < input.org
<strong>hello world!</strong>

カンマ , を含む文字列をマクロの引数として渡すには、エスケープして \, とします:

$ cat input.org
#+MACRO: tag <$1>$2</$1>
<<<tag(strong,hello\, world!)>>>
$ gokuro < input.org
<strong>hello, world!</strong>

マクロの本体に $0 が含まれると、引数を表す括弧の中の文字列そのもので置き換わります。カンマがあってもそのままです:

$ cat input.org
#+MACRO: explain 引数は $0 です
<<<explain(x,y,z)>>>
$ gokuro < input.org
引数は x,y,z です。

定義されていないマクロの呼び出しは削除されます:

$ cat input.org
This macro is <<<undefined>>>.
$ gokuro < input.org
This macro is .

マクロの呼び出しは一行ごとに一番後ろにあるものから一つずつ処理されます。マクロを置き換えによって新たにマクロの呼び出しが生まれれば、それも処理の対象となります:

$ cat input.org
#+MACRO: POW_1 2
#+MACRO: POW_2 4
#+MACRO: POW_3 8
#+MACRO: explain-pow 2 の $1 乗は <<<POW_$1>>> です。
<<<explain-pow(1)>>>
<<<explain-pow(2)>>>
<<<explain-pow(3)>>>
<<<explain-pow(4)>>>
$ gokuro < input.org
2 の 1 乗は 2 です。
2 の 2 乗は 4 です。
2 の 3 乗は 8 です。
2 の 4 乗は  です。

#+MACRO: NAME BODY という行が定義するのは “グローバルな” マクロであり、その行より後ろの任意の位置で有効になります。これに対して #+MACRO_LOCAL: NAME BODY とすると “ローカルな” マクロを定義できます。ローカルなマクロは次の行でのみ有効になります。

$ cat input.org
#+MACRO: tag <$1<<<HTML_ATTR>>>>$2</$1>
<<<tag(strong,hello world!)>>>
#+MACRO_LOCAL: HTML_ATTR  style="color: red;"
<<<tag(strong,hello red world!)>>>
<<<tag(strong,hello world!)>>>
$ gokuro < input.org
<strong>hello world!</strong>
<strong style="color: red;">hello red world!</strong>
<strong>hello world!</strong>

マクロの呼び出しによって入力行が (グローバルまたはローカルな) マクロの定義になった場合、その行はマクロの定義として扱われます。出力もされません:

$ cat input.org
#+MACRO: tag <$1<<<HTML_ATTR>>>>$2</$1>
#+MACRO: color #+MACRO_LOCAL: HTML_ATTR  style="color: $1"
<<<color(red)>>>
<<<tag(strong,hello red world!)>>>
$ gokuro < input.org
<strong style="color: red;">hello red world!</strong>

文法と動作の説明はこれで以上です。これ以外の特殊な構文や動作はありません。

最後に、HTML と LaTeX の両方に変換できるファイルの例を示します:

#+MACRO: double-quote <<<html(“$0”)>>><<<latex(``$0'')>>>
#+MACRO: italic <<<html(<i>$0</i>)>>><<<latex(\textit{$0})>>>
#+MACRO: ruby <<<html(<ruby>$1<rt>$2</rt></ruby>)>>><<<latex(\ruby{$1}{$2})>>>
#+MACRO: image <<<html(<figure <<<HTML_ATTR>>>><img src="$1"></figure>)>>><<<latex(\includegraphics<<<LATEX_OPTION>>>{$1})>>>

<<<double-quote(クオート)>>> と <<<italic(斜体)>>> と <<<ruby(ルビ,るび)>>> のマクロが定義されています。

#+MACRO_LOCAL: HTML_ATTR style="width: 400px;"
#+MACRO_LOCAL: LATEX_OPTION [width=0.75\linewidth]
<<<image(figure.png)>>>

これを input.org と保存すると、次のコマンドで HTML に変換できます:

$ echo "#+MACRO: html $0" | cat - input.org | gokuro

“クオート” と <i>斜体</i> と <ruby>ルビ<rt>るび</rt></ruby> のマクロが定義されています。

<figure style="width: 400px;"><img src="figure.png"></figure>

LaTeX にエクスポートするには次のようにします:

$ echo "#+MACRO: latex $0" | cat - input.org | gokuro

``クオート'' と \textit{斜体} と \ruby{ルビ}{るび} のマクロが定義されています。

\includegraphics[width=0.75\linewidth]{figure.png}

gokuro の処理系

gokuro の処理系の例を示します。この処理系 gokuro は標準入力から入力を読み、一行ごとに処理を行い、結果を標準出力に書き出します。gokuro が各行に行う処理の概要を説明します。

  1. 入力行が #+MACRO: NAME BODY という形をしているなら、名前が NAME で本体が BODY のグローバルなマクロを定義し、5 に進む。
  2. 入力行が #+MACRO_LOCAL: NAME BODY という形をしているなら、名前が NAME で本体が BODY のローカルなマクロを定義し、5 に進む。
  3. 入力行にマクロ呼び出し (<<<NAME>>> または <<<NAME(ARGS)>>> という形の文字列) が含まれないなら、入力行を出力し、5 に進む。
  4. 入力行にある最後のマクロ呼び出しを本体で置き換える。1 に戻る。
  5. この行でローカルなマクロが定義されていないなら、ローカルなマクロをクリアする。
  6. 次の行ヘ進む。

簡略化した gokuro の処理を表す疑似コードを示します:

void gokuro(void) {
  global_macro = []
  local_macro = []
  while (stdin が空でない) {
    line = stdin から一行読む
    line_type = LINE_NORMAL
    while (true) {
      if (line  "#+MACRO: <<NAME>> <<BODY>>" という形をしている) {
        global_macro[<<NAME>>] = <<BODY>>
        line_type = GLOBAL_MACO_DEFINITION
        break
      }
      if (line  "#+MACRO_LOCAL: <<NAME>> <<BODY>>" という形をしている) {
        local_macro[<<NAME>>] = <<BODY>>
        line_type = LOCAL_MACO_DEFINITION
        break
      }
      if (line にマクロ呼び出しが無い) {
        line_type = LINE_NORMAL
        break
      }
      macro_call = line に含まれる最後のマクロ呼び出し
      macro_body = ""
      if (local_macros[macro_call.name] が存在する) {
        macro_body = local_macros[macro_call.name]
      }
      if (global_macros[macro_call.name] が存在する) {
        macro_body = global_macros[macro_call.name]
      }
      replacement = macro_body 内の変数を macro_call.args で置き換える
      line = line  macro_call  replacement に置き換える
    }
    write(stdout, line)
    local_macro = []
  }
}

関数型っぽく書くとこうなります:

let rec gokuro () =
  let rec repl global_macro local_macro =
    match read_stdin() with
    | EOF -> ()
    | line ->
      let g, l = process_line global_macro local_macro line in
      repl g l in
  repl (Hash.new ()) (Hash.new ())

let rec process_line global_macro local_macro line =
  match line with
  | "#+MACRO: " + name + " " + body 
    -> (Hash.add global_macro name body), (Hash.new ())
  | "#+MACRO_LOCAL: " + name + " " + body
    -> global_macro, (Hash.add local_macro name body)
  | line
    -> let name, args = get_last_macro line in
       if name == null
       then
         print_line line
         global_macro, (Hash.new ())
       else
         let body = match (Hash.get local_macro name) with
                    | Some x -> x
                    | None -> match (Hash.get local_macro name) with
                              | Some x -> x
                              | None -> "" in
         let line = (substitute_macro line name body args) in
         process_line global_macro local_macro line

想定される質問への回答

gokuro の解説は以上です。以降は設計・実装するときに考えたことなどを書いておきます。

Q. なぜ gokuro を作ったのですか?

A. 必要だったからです。このサイトは最初 emacs の org-mode を使って作られていましたが、emacs 付属のプログラムが大きなテキストに対して非常に遅かったので、エクスポートする部分を Go を使ったプログラムで書き換えました。その Go プログラムから独立したのが gokuro です。

Q. gokuro という名前の由来は何ですか?

A. Go で (最初に) 書かれたマクロ言語なのでゴクロです。gocro という名前の会社があるようなので英語表記は gokuro としました。

Q. 私は Pandoc を使っているのですが、gokuro を併用することはできますか?

A. できます。gokuro を最初に使ってから Pandoc に入力を渡してください:

$ gokuro < INPUT_FILE | pandoc -f markdown -o OUTPUT_FILE

Pandoc に記法が用意されていない HTML タグや LaTeX 命令を使ったり、文章中に繰り返し登場する構造 (例えば “練習問題” や “定理” など) を統一的に記述するのに gokuro が使えます。

Q. gokuro で書いたファイルの拡張子は何にするべきですか?

A. org-mode と同じ拡張子 .org を使うことをお勧めします。この上でエディタに org-mode 用のプラグインを入れるとシンタックスハイライトが働きます。

Q. emacs で大きな gokuro ファイルを開いたら重いのですが。

A. init.el(setq org-radio-target-regexp "$^") を追加してください。gokuro のマクロ呼び出し <<<...>>> は org-mode では radio link を表すので、そのままだと余計な処理が行われます。


Q. Markdown, AsciiDoc, org-mode, reStructuredText, Re:VIEW といった軽量マークアップ言語と gokuro は何が違うのですか?

A. gokuro は機能を極限まで削っています。gokuro の機能はマクロの定義と呼び出しだけであり、この二つが分かれば gokuro ファイルを読み書きできるようになります。

これに対して上に示した軽量マークアップ言語には HTML タグや LaTeX 命令に対応する記法があり、言語ごとにドキュメントを見て覚える必要があります。記号文字を使った記法 (=...=*...*) は検索しにくいことも多く、手間です。

Q. なぜ機能を削ったのですか?

A. 機能を削ると実装しやすいからです。gokuro は最初 Go で実装されていましたが、速度を上げるために C で実装され、その後ブラウザから利用できるように JavaScript で実装されました。他のマークアップ言語のように機能をたくさん持っていたとしたら、三つの言語で実装するのは (予算・時間・技量的に) 無理だったでしょう。

さらに機能を削れば実装も高速になります。gokuro は他のプログラム (LaTeX や Pandoc) と協調して動作することを前提としているので、実行時間が長くなることは受け入れられません。

Q. 機能を削って不便にならないのですか?

A. 他の言語を本格的に使ったことがないので、他と比べてどうなのかは分かりません。ただ何冊かの本の PDF (LaTeX 経由) と HTML (Hugo 経由) を一つのファイルから出力することに成功しているので、個人的には十分使えます。

Q. m4 を使えるのではないですか?

A. m4 にはマクロを定義して呼び出す機能があり、マクロになっていない部分はそのまま出力されます。そのためある意味では m4 と gokuro は似ていると言えます。しかし m4 はマルチバイト文字を正しく扱えないので、日本語の文章には向いていません。

Q. XML を使えるのではないですか?

A. 使えると思います。実際私が今現在書いているのは見た目が変わった XML のようなものです。XML なら仕様も決まっていて、たいていの言語で利用可能で、変換のための言語 (XSLT) さえあります。ただ XML でエクスポーターを書いたとして gokuro と同じ速度が出るかどうかは疑問です。また原稿ファイルから LaTeX や HTML への変換では大部分が入力と同じ文字列となるので、そのような場合に XML や XSLT を使うのはやり過ぎな気がします。

Q. Markdown, Re:VIEW, org-mode といった他の軽量マークアップ言語では文字の装飾やリンクのための記法が用意されていますが、gokuro に存在しないのはなぜですか?

A. マクロを使えば必要ないからです。例えば Markdown の [link text](link url)

#+MACRO: link <<<html(<a href="$1">$2</a>)>>><<<latex(\href{$1}{$2})>>>
<<<link(link url,link text)>>>

とできます。特別な記法を用意すると実装が複雑になるので用意していません。こうするとマクロで表現できない機能 (ブックマークなど) が必要になったときに困りますが、そのときには出力先フォーマット (LaTeX や HTML) の機能を使えば解決できます。

さらに様々な要素をマクロで定義しておけば文書が柔軟になります。例えば「HTML のリンクは target="_blank" にする」や「PDF ではリンクに下線を引く」といった変更が後から一括で行えます。特別な記法を導入するとこれは難しくなります。


Q. 複数行に渡るマクロを定義できますか?

A. できません。

Q. 複数行に渡るマクロを定義できないのはなぜですか?

A. 一番の理由は入力ソースが複数になって実装が複雑になるからです。実装が複雑になると速度が落ちて移植が難しくなるので受け入れられません。さらに HTML と LaTeX (私が使うときのエクスポート先言語) ではタグ/命令を改行無しに続いて書いてもたいていの場合は意味が変わらないので、複数行のマクロを実装しなくても一行が長くなるだけで問題は起こりません。加えて複数行のマクロを使いたいと思ったことがほとんどありません。また複数行のマクロがあると、理解不可能なほど複雑なマクロが書けるようになってしまうでしょう。

Q. どうしてもマクロの本体に改行を入れたいのですが。

A. マクロの本体に適当な文字列を印として入れておいて、gokuro の後に置換してください。例えば

$ cat input.org
#+MACRO: multiline-macro one@@newline@@two@@newline@@three
<<<multiline-macro>>>

としておけば

$ cat input.org | gokuro
one@@newline@@two@@newline@@three
$ cat input.org | gokuro | sed s/@@newline@@/\\n/g
one
two
three

とできます。ただしこうしても複数行に渡るマクロの本体でマクロを定義するのは不可能です。諦めるか、自分でプログラムを改変してください。


Q. ローカルなマクロは何のためにあるのですか?

A. 稀にしか使われないマクロの引数を扱うためです。例えば

#+MACRO: tag <$1<<<HTML_ATTR>>>>$2</$1>

というマクロにおける HTML_ATTR は空文字列であることがほとんどなので、tag マクロの引数とするよりローカルなマクロを使う方が便利です。もちろん引数として毎回空文字列を渡しても構いません。

Q. ローカルなマクロは引数を使えば必要無いのに、どうして存在するのですか?

A. マクロの引数が覚えきれないほど多くなる場合があるからです。例えばローカルなマクロを追加していくと

#+MACRO_LOCAL: WIDTH 500px
#+MACRO_LOCAL: CAPTION_TEXT キャプション 1
#+MACRO_LOCAL: LATEX_OPTION [origin=lB]
#+MACRO_LOCAL: HTML_ATTR  style="border: solid 2px"
<<<image(figure_1.png)>>>

#+MACRO_LOCAL: WIDTH 500px
#+MACRO_LOCAL: HEIGHT 800px
#+MACRO_LOCAL: ALT_TEXT 代替テキスト 2
<<<image(figure_2.png)>>>

#+MACRO_LOCAL: HTML_ATTR  style="display: flex;"
#+MACRO_LOCAL: CAPTION_TEXT キャプション 3
#+MACRO_LOCAL: ALT_TEXT 代替テキスト 3
<<<image(figure_3.png)>>>

のような状況になることがありますが、これをローカルなマクロ無しに引数だけを使って書くのは大変です。

Q. マクロの引数が覚えられなくなるほど多いのは、マクロの設計に失敗しているからではないですか?

A. 多分そうです。ローカルなマクロは失敗した設計を補填するためのモンキーパッチ (あるいは完璧な設計を最初から放棄するための言い訳) と言えます。

本は完成した後に大きく変更されることは少ないので、マクロの設計を汚くしてでも完成を優先させる選択肢があってもよいだろうと私は判断しました。


Q. 定義したマクロを削除できますか?

A. できません。ローカルなマクロを使うか、本体が空文字列のマクロを再定義してください。

Q. 定義したマクロを削除できないのはなぜですか?

A. 実装が面倒だからです。削除するための組み込みの命令を用意して、さらにゴミ集めを実装する (あるいはメモリの断片化を許す) のは負担が大きすぎます。

これに対してローカルなマクロは必ずまとめて削除されるので、メモリの断片化無く簡単に実装できます。


Q. マクロを定義していない文書を入力したら入力と出力が一致しなかったのですが、なぜですか?

A. マクロの呼び出し <<<...>>> とみなされる文字列が含まれているのだと思います。gokuro は定義されていないマクロの呼び出しを削除します。

Q. 定義されていないマクロの呼び出しが削除されるのはなぜですか?

A. そうした方が実装しやすいからです。未定義マクロの呼び出しをそのままにするには「処理中の行に含まれる未定義のマクロ」を覚えておく必要があり、実装が面倒です。

Q. このページには <<<...>>> が含まれていますが、これはどうやって入力したのですか?

A. 適当にエスケープしてから置換しました:

$ gokuro < INPUT_FILE | sd "{,{,{" "{{{" | sd "},},}" "}}}" > OUTPUT_FILE

Q. マクロの構文をエスケープする方法を gokuro で用意しないのですか?

A. しません。

Q. なぜですか?

A. 衝突はまず起こらないからです。実装も面倒です。

gokuro の文法と衝突する可能性のある (意味のある) 文字列としては C++ のテンプレートがあります。例えば <<<mono(std::vector<std::shared_ptr<Widget<double>>>)>>> とすると衝突します。私はこういったコードを含んだ文書は書かないので、これを受け入れました。もし C++ テンプレートメタプログラミングに関する文書を gokuro で書くのであれば、上で示した置換を用いる方法を使ってください。


Q. マクロの文法が見慣れないのですが、どうしてこうしたのですか?

A. org-mode のマクロを参考にしています。

Q. org-mode では {{{...}}} としてマクロを呼び出すようですが、どうして gokuro では <<<...>>> に変えたのですか?

A. {{{...}}} は LaTeX のコードと容易に衝突するからです。例えば

#+MACRO: big {\LARGE $0}
{{{big(\smash{\underline{\textbf{This word}}} is important)}}}

と書くと、これは big というマクロに \smash{\underline{\textbf{This word}}} is important を渡しているのではなく、big(\smash{\underline{\textbf{This word というマクロを呼び出しているものとして処理されます (もちろんこんな名前のマクロは定義されていないので、この部分は無くなります)。この衝突がかなり頻繁に起きたので、構文を <<<...>>> に変えました。

Q. マクロを呼び出す構文 <<<NAME>>> が長すぎると思うのですが。

A. 前述の通り gokuro は未定義のマクロを削除するので、衝突しやすい構文を使うことはできませんでした。gokuro で文書を書くときはエディタのスニペット機能の利用をお勧めします。


Q. 行の最後にあるマクロから処理するのはなぜですか?

A. その方が直感的だからです。例えば入力が

<<<CHAPTER_NAME_<<<CHAPTER_NUMBER>>>>>>

のときに、「CHAPTER_NAME_<<<CHAPTER_NUMBER というマクロは定義されていないから削除して、出力は >>>」と処理するのは不自然です。行ごとに最後のマクロから処理を行うと直感的になります。

Q. 組み込みのマクロが無いのはなぜですか?

A. gokuro は入力と出力のフォーマットを想定しないので、組み込みにすべきマクロが定まらないからです。また実装も面倒です。

Q. マクロの定義を組み込みのマクロとして実装しないのはなぜですか?

A. マクロの本体にマクロの呼び出しを含められるようにするためです。もしマクロとして実装すると

<<<DEFINE_MACRO(MACRO_NAME,<<<value>>>)>>>
-> <<<DEFINE_MACRO(MACRO_NAME,)>>>
-> (マクロが定義される)

となり、マクロの本体に含まれるマクロの呼び出しが全て処理されてからマクロが定義されることになります。これは意図した動作ではありません。

Q. マクロの処理結果でマクロを定義できるのはなぜですか?

A. ローカルなマクロを定義する構文は少し長いので、定義をマクロを使って書くことがあったからです。例を示します:

#+MACRO: tag <$1<<<HTML_ATTR>>>>$2</$1>
#+MACRO: color #+MACRO_LOCAL: HTML_ATTR  style="color: $1"
<<<color(red)>>>
<<<tag(strong,hello red world!)>>>

Q. gokuro の構文が変更される可能性はありますか?

A. 致命的な欠陥が見つからない限り構文は変えません。

Q. 今後 gokuro に新しい機能を追加しますか?

A. そのつもりはありません。新しい機能が必要になったときは外部プログラム (LaTeX や Pandoc) に頼るか、個別のプログラムを書いてパイプで繋ぐようにします。万一外部プログラムでは対処できない問題が生じた場合にはもしかしたら機能を追加するかもしれませんが、その場合でも後方互換性をなるべく保つようにします。

Q. カウンターは使えますか?

A. gokuro でカウンターを普通に実装することは (おそらく) できません。しかし効率を犠牲にすれば次のように実装できます:

#+MACRO: TO_ARABIC. 1
#+MACRO: TO_ARABIC.. 2
#+MACRO: TO_ARABIC... 3
#+MACRO: TO_ROMAN. I
#+MACRO: TO_ROMAN.. II
#+MACRO: TO_ROMAN... III
#+MACRO: TO_KANJI. 一
#+MACRO: TO_KANJI.. 二
#+MACRO: TO_KANJI... 三
#+MACRO: def #+MACRO: $1 $2
#+MACRO: counter_new #+MACRO: COUNTER_$1 .
#+MACRO: counter_value <<<TO_$1<<<COUNTER_$2>>>>>>
#+MACRO: counter_increment <<<def(COUNTER_$1,<<<COUNTER_$1>>>.)>>>

<<<counter_new(chapter)>>>
章の始まり
Chapter <<<counter_value(ARABIC,chapter)>>>.
Chapter <<<counter_value(ROMAN,chapter)>>>.
第<<<counter_value(KANJI,chapter)>>>章

次の章の始まり
<<<counter_increment(chapter)>>>
Chapter <<<counter_value(ARABIC,chapter)>>>.
Chapter <<<counter_value(ROMAN,chapter)>>>.
第<<<counter_value(KANJI,chapter)>>>章

章の始まり
Chapter 1.
Chapter I.
第一章

次の章の始まり
Chapter 2.
Chapter II.
第二章

ただ定義されるマクロが多いので、カウンターが数百まで増えるような場合には対応できないでしょう。諦めて他のプログラムを使ってください。


Q. gokuro の処理系を実行したらパソコンがぶっ壊れました。

A. gokuro の処理系は無保証で提供されます。

まとめ