引用元:https://www.ioccc.org/1989/jar.2.c
審査員・作者による説明:https://github.com/ioccc-src/winner/blob/main/1989/jar.2.hint
動作
Lispインタプリタ。
$ gcc -o jar.2 jar.2.c
$ echo "(+ 1 1") | ./jar.2
$ cat fib.lisp
(defun fib (n)
(if (< n 2)
1
(+ (fib (- n 2)) (fib (- n 1)))))
(fib 10)
$ cat fib.lisp | ./jar.2
89.000000
解説
初の言語処理系作品。1465バイトに次の機能が盛り込まれているとのこと。
+ - * < ()
car cdr cons defun equal
function if lambda quote t
jar.2.hintにかんたんなサンプルコードがいくつか掲載されている。
エラーチェックは非常に甘く、たとえば最初のカッコの前にスペースがあったら動かないとのこと。
形状の意味は特に解説されておらず不明だが、カッコを表現しているだろうか(下のほうがよくわからないが)。
上下に挟まれた行のアルファベットだけ拾い読みするとO B F U S C A T E D L i s p
になっている。
,O;}B(F)U S(C(A(T(E)?D:_)));}L(i,s)p
作者のコメントによると、「Lispはコンスセルを基本データとするが、このプログラムはchar*
を基本とする」と書かれている。
つまり単なるベタメモリということで、基本的にはただのジョークだと思うけれど、実際、組み込み関数のテーブルは{関数名,関数ポインタをchar*にキャストしたもの}
の列となっている(構造体を宣言する必要がなくなるのでコードが小さくなる効果はあると思う)。
オリジナルのソースコードは、グローバル変数の配列が連続領域に確保されていることを前提としている。
p r[4][2]={...};
とp not[99][2]={...};
とう定義に対してr[13]
でnot[9]
をアクセスする。
このようにした理由はわからない(特に小さくなるわけでも、レイアウトしやすくなるわけでもないと思うし、not
は直接アクセスされていない)。
単に難読化のためだろうか。
このせいで現代のコンパイラで最適化オプション付きでビルドするとsegfaultになって動かないので、ひとつの定義とするように修正をおこなった。
[[1989/jar.1]]と合わせ、同じ年に同じ作者の作品が複数入賞した初の事例。
パッチ
パッチをダウンロード