「文言」（漢文プログラミング言語）の「獺祭」について

# いままでのあらすじ
プログラミング経験の無い[友人](https://qiita.com/S_Y15)と通話していたところ、彼は[「文言」（漢文プログラミング言語）](https://wy-lang.org/)で[プログラミングを始めてしまいました](https://qiita.com/S_Y15/items/44be8cfc67a18bbcd2bb)。これは3割ぐらい私のせいなので、責任を取って私も始めることにしました。

公式サイトを見たところ、

> wenyan currently compiles to JavaScript, Python, or Ruby, and will support more languages (e.g. C) in the future. 

と書いてあったので、「よし、じゃあRustへのコンパイラを書くか。Rustで。」という気持ちになりました。ということでそれを書いています。[GitHubリポジトリはこちら](https://github.com/sozysozbot/wenyan-to-rust)。2020年7月22日現在、[愛おしい教科書](https://book.wy-lang.org/)の「算術第三」まではコンパイルできるようになっています。

# 内容
今回は、これを実装する上で「仕様を読まずに空気を読む」で組んだ結果だいぶ仕様の理解に苦労し、最終的に教科書（の「變數第二」）に答えの書いてあった仕様である、「獺祭」について解説していきたいと思います。

# 前提知識

『[はじめてのプログラミングを「文言」（漢文プログラミング言語）で　１日目](https://qiita.com/S_Y15/items/28d5a35941f33fe44512)』は読んできていることを前提とします。もしくは[原文の明義第一](https://github.com/wenyan-lang/book/blob/master/01%20%E6%98%8E%E7%BE%A9%E7%AC%AC%E4%B8%80.md)。

# 「獺祭」？
日本では[酒の名前](https://www.asahishuzo.ne.jp/index.php)として有名ですね[^1]。なぜそれが仕様の名前なのでしょうか。

実は「獺祭」または「獺祭魚」というのは礼記に出てくる言葉で、自分が捕まえた魚を並べるカワウソの習性を~~祭り~~供え物（[Mag462さん](https://twitter.com/Magnezone462)ご指摘ありがとうございます）に喩える用語だそうです。これから解説する仕様は「變數第二」の末尾に説明されているのですが、この「獺祭」の典故を知らないと意味が取れません。私に漢籍の教養がないことを恥じるばかりです。

# 教科書読解

さて、読んでいきましょう。文脈としては、

```
加一以三。加六以九。名之曰「甲」。曰「乙」。
```

と書くことによって変数「甲」に1+3、「乙」に6+9が入る、という説明がされた後で、

> 凡未名之變數。皆如獺祭然。言`其`者。取至近之魚而棄其餘。言`書之`者。盡書之。言`名之`者。取若干而名之。曰。然也。善哉此比。
> 
> 問曰。每有未名者。輒祭如是。豈非終累累然焉。今欲盡棄其魚。復當作何書。曰。當書`噫`。`噫`者。嘆辭也。所以嘆彼之盡棄也。

という記述があります。先程の典故を踏まえると意味を取ることができて、

* 名前のついていない変数は、カワウソが魚を並べるように並んでいる。
* `其`は、最も近くの魚（=名前のついていない変数）を取って残りを捨てる。
* `書之`は、これ（=並んでいる魚）を全て書く。
* `名之`は、何個かを取ってこれを名付ける。
* 魚を捨てたいときには、`噫`と書け

ということが書いてあります。

先程の例を見ると、

```
加一以三。加六以九。名之曰「甲」。曰「乙」。
```

は`加一以三。`で魚`4`が並べられ、`加六以九。`で魚`15`が並べられ、`名之曰「甲」。曰「乙」。`で魚2匹が消費され変数「甲」と変数「乙」に値が代入される、ということです。

# 例

せっかくなので、幾つか例を見ていきましょう。

## 例1
```
加二以三。加一以三。加三以三。書之。書之。
```
魚が`5`・`4`・`6`の順に並べられ、最初の`書之`がこれらを全て消費し`五 四 六`を出力。次の`書之`は消費すべき変数がないので空文字列を出力する。

## 例2
```
加二以三。加一以八。減其以七。書之。
```
魚が`5`・`9`の順に並べられ、次の`減其以七`にある`其`は川岸から`9`を取得して`5`を捨てる。よって`9`から`7`が引かれた`2`のみが川岸に置かれるので、`書之`により`二`が出力される、

## 例3
```
加二以三。加一以三。加三以三。名之曰「甲」。曰「乙」。書之
```
これは、魚が`5`・`4`・`6`の順に並べられ、次の`名之曰「甲」。曰「乙」。`によって**最も近くの二つ**である`4`と`6`がそれぞれ「甲」と「乙」に。川岸にはまだ`5`が残っているので、それを`書之`が出力する。

## 例4
```
加一以三。加六以九。名之曰「甲」。名之曰「乙」。
```
これは先程見た `加一以三。加六以九。名之曰「甲」。曰「乙」。`に**非常に似ている**が、なんと挙動が異なる。今回は魚`4`と魚`15`が並べられたあと、`名之曰「甲」。`が**最も近く**の魚を**一つ消費**するので、「甲」には15が入り、次の`名之曰「乙」`で「乙」に4が入る。

**紛らわしい**。私はこれで1回バグらせた。

## 例5
```
加一以三。加二以三。減其以其。書之。
```
これは`4`と`5`が川岸に置かれたあと、`減其以其`の最初の`其`は`5`を取得し、**同時に魚を全部捨てる**。よって次の`其`は何も取得できない[^2]。結果として計算は上手く行かず、`不可算數`かなんかが出力される。

## 例6
```
吾有一言。曰「「天地。」」。
為是三遍。
	書之。
	吾有一言。曰「「問天地好在。」」。書之。
云云。
```
まず文字列「「天地。」」を川岸に置いたあと、ループが始まり、直後に`書之`がある。これは川岸に置かれた列「「天地。」」を指し、**これを指し続ける**。結果、出力されるのは

> 天地。
> 問天地好在。
> 天地。
> 問天地好在。
> 天地。
> 問天地好在。

となる。

# 余談（というか愚痴）

JavaScriptくんで自由に使える `undefined` とかいうものの意味論をRustに移すのがわりとつらいです。ReferenceErrorが実行時にcatchできるのもつらそうです。`吾有一言。名之曰三。`を本家実装くんが平気で`var 3 = "";`とかコード生成してくれるのもつらいです。[issue・プルリクお待ちしています](https://github.com/sozysozbot/wenyan-to-rust)。

[^1]: 私は酒を一切飲まないので詳しいことは知りません。
[^2]: 本家実装は `加一於三。減其於其` も `加一於三。減其以其` も共に `const _ans1 = 3 + 1; const _ans2 = _ans1 - undefined;` にコンパイルされるのだけど、厳密にいうと意味論的には片方は `undefined - _ans1` であるべき……だけどまあ必ずNaNになるんだし、まあ、うーん