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


いままでのあらすじ

プログラミング経験の無い友人と通話していたところ、彼は「文言」(漢文プログラミング言語)プログラミングを始めてしまいました。これは3割ぐらい私のせいなので、責任を取って私も始めることにしました。

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

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

と書いてあったので、「よし、じゃあRustへのコンパイラを書くか。Rustで。」という気持ちになりました。ということでそれを書いています。GitHubリポジトリはこちら。2020年7月22日現在、愛おしい教科書の「算術第三」まではコンパイルできるようになっています。

内容

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

前提知識

はじめてのプログラミングを「文言」(漢文プログラミング言語)で 1日目』は読んできていることを前提とします。もしくは原文の明義第一

「獺祭」?

日本では酒の名前として有名ですね1。なぜそれが仕様の名前なのでしょうか。

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

教科書読解

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

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

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

凡未名之變數。皆如獺祭然。言者。取至近之魚而棄其餘。言書之者。盡書之。言名之者。取若干而名之。曰。然也。善哉此比。

問曰。每有未名者。輒祭如是。豈非終累累然焉。今欲盡棄其魚。復當作何書。曰。當書者。嘆辭也。所以嘆彼之盡棄也。

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

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

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

先程の例を見ると、

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

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

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

例1

加二以三。加一以三。加三以三。書之。書之。

魚が546の順に並べられ、最初の書之がこれらを全て消費し五 四 六を出力。次の書之は消費すべき変数がないので空文字列を出力する。

例2

加二以三。加一以八。減其以七。書之。

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

例3

加二以三。加一以三。加三以三。名之曰「甲」。曰「乙」。書之

これは、魚が546の順に並べられ、次の名之曰「甲」。曰「乙」。によって最も近くの二つである46がそれぞれ「甲」と「乙」に。川岸にはまだ5が残っているので、それを書之が出力する。

例4

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

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

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

例5

加一以三。加二以三。減其以其。書之。

これは45が川岸に置かれたあと、減其以其の最初の5を取得し、同時に魚を全部捨てる。よって次のは何も取得できない2。結果として計算は上手く行かず、不可算數かなんかが出力される。

例6

吾有一言。曰「「天地。」」。
為是三遍。
    書之。
    吾有一言。曰「「問天地好在。」」。書之。
云云。

まず文字列「「天地。」」を川岸に置いたあと、ループが始まり、直後に書之がある。これは川岸に置かれた列「「天地。」」を指し、これを指し続ける。結果、出力されるのは

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

となる。

余談(というか愚痴)

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


  1. 私は酒を一切飲まないので詳しいことは知りません。 

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