ref: http://ioccc.org/2013/endoh1.c
#ifndef SKI #define A(x)B(x##0)B(x##1)B(x##2)B(x##3)B(x##4)B(x##5) #define B(x)C(x##0)C(x##1)C(x##2)C(x##3)C(x##4)C(x##5) #define C(x)D(x##0)D(x##1)D(x##2)D(x##3)D(x##4)D(x##5) #define D(x)Z(x##0)Z(x##1)Z(x##2)Z(x##3)Z(x##4)Z(x##5) #define p(x)(*(*(*(*(*(*x)())())())())())() #include <stdio.h> #include <stdlib.h> #define S (Y(s+6)) #define K (Y(s+4)) #define I (Y(s+2)) #define Z(x)\ f x;f Y(v);g\ j;f \ x##z( f\ y){y =y?! x?x= y:Y( m(*( w)&\ x,(( g)y) (0)) ):x;return y; /*ab c*/} /**/ typedef void *v ;int n=0; typedef v* w;typed\ ef v(*g)();v s[]= {0,0, s+6, s+2, s+4,s,s+3,s+ 5,s+ 1};w m( v l ,v r){w e=ma\ lloc (siz\ eof( v)*3 );*e =r;e [1 ]=l;return (e[2 ]=s, e);} /*de fghi jk*/ typ\ edef v(p( p(p( p(p( p(p( p(p( p(p(f)))) ))) )))) );A( x)w u(){ return n --?m (j(0 ),u( )):s+3;} w z( w e) {w a,b , c,d;for (d =e=m(e,0 );n= (w)d[ 2]-s ,n?* (n>1? n<4? b:a: c)?n <2?c[ 1]=m (*a, *c), *c=m (*b, *c): n>5? #undef Z #define Z(x\ ) &x ##z, a[1]= m( *a,!*d? d[1] =c=m (0,0 ),n= getchar(),n= n<0? 256:n,c [2]= s+6, *d=u ():* d),*a=d[1]: n<5?n<3?n=z(* a)-s, putchar( (255 <n)? exit (n-4 *64) ,0:n ),fflush (stdout) ,c[1 ]= *b:((n-3?b:c )[1] =*a) :(2[* a=z(*a)+1,a ]=s+5),d =e:0 :e;d =d[1 ])c=b,b=a,a= d;a= *(w* )e [1 ]; ;; /*lm no*/ return a;}f( *h [])( )={A (x)0};f Y(v x) {g y =(g) h[n++] ;y(! y? exit( puts ("out of c" "" "losure" )),x :x );return(f )y/* pq r*/;}int main () {z(((g )(S S( j=(g )S(S (K S)K))(K( S I I)) (S(K(S I))( S( K K) (S(K (S(S(K(Y(s+1 )) )(S( S S(K I)) (K (Y (s +5)) )) )) )K)) )( #endif #if (!defined(__INCLUDE_LEVEL__) || !__INCLUDE_LEVEL__) && !defined(SKI) K(I(I(I(S I I(S(K(S(S(K S)(S(S S S(K(S(K S)))(S(S S S(K(S(K S)))(S(K(S(S(K S)(S(S(K S)K)(K(S(S(K S)(S(K(S(S(S I(K K))(K K))))(S(K K)(S(S(K S)(S(K(S(S I(K K))))(S(K K)(S(S(K K))(K(S(S(S(S(K S)K)))I(S(S(K S)K)I)))))))(S(K K)(S(S(K K))(K(S(K(S(S(K S)K)I))(S(S(K S)K)(S I I(S(S(K S)K)I)))))))))))(S(K K)(S(K K)(S(S(K K))(K(S(K(S(S(K S)K)I))(S(S I I)I(S(S(K S)K)I)))))))))))))(S(K K)(S(K K)(S(K(S(S I(K(S(S I(K(K I)))(K S))))))K)))))(K(K(K K)))))(K(K(K(K I))))))))(S(K(S(K K)))(S(S(K S)(S(K K)(S(K S)(S(S(K K))I))))(K(S(K(S(S(K S)(S(K(S I))K))))(S(K K)K))))))(S(S I(K(S I I(S I I(S(S(K S)K)I)))))(K(K K)))S K K K I I I K K K I I I K K K I I I K K K I I K K K K K S K I I I K I K I I I K I K)I I I K I K I I I K I I I K I I S K I I I I I K I I I I I K I K I I I K I I I K I I S K I I)I K I K I I I K I K I I I K I K I I I K I I I K I I S K K K I I I K K K I I I K K K I I I K K K I I K K K K K S S K I I I K S)K I I K S K K K S K I I K S K I I I K S S K K K K K S K I I I I S K K K K K S K S K K K K K(K K)) #define SKI #endif #ifdef SKI (Y(s)))))(0));return 0;} #else #define SKI #endif
使い方
単体でもコンパイル・実行できるんですが、
$ gcc -o endoh1 endoh1.c $ ./endoh1 @@@@@ @ @@@@@ @ @@@@@ @ @ @ @ @@@ @ @ @ @ @@@@@ @@@ @@@ @@@ @@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@@@ @@@ @@@ @@@ @@@
これは本題ではありません。
このプログラムは、C コンパイラに Lazy K のコードを食わせていじめるためのライブラリです。これがあれば、C コンパイラしかない状況で Lazy K を楽しむことができます。*1
Lazy K ってのは SKI コンビネータを遅延評価で云々という言語なんですが*2、要するに関数 3 つだけ (S と K と I) の難解言語です。見た目はこんな感じ。
K(S(S I(K(S(K(S I I(S(S(K S)K)I))) ... ))))
これの最初と最後に #include
#include <endoh1.c> K(S(S I(K(S(K(S I I(S(S(K S)K)I))) ... )))) #include <endoh1.c>
これだけで valid な C プログラムになります。この通り。
$ gcc -o hello -xc hello.lazy $ ./hello Hello, IOCCC!
変な行入れたら Lazy K じゃなくなるじゃないか!という Lazy K ファンの方もご安心を。Lazy K において # は行コメントですので、そのまま実行できます。
$ lazy hello.lazy Hello, IOCCC!
つまり、Lazy K と C の polyglot (と言えなくもない) です。
解説
#define S (s) #define K (k) #define I (i)
とマクロ定義することで
S (K I)
が
(s) ((k) (i))
となり、冗長な括弧をとると
s(k(i))
あれっこれ普通の C じゃん、というのが肝です。ここから苦労した点はいっぱいあって
- C に再帰型がない → 「関数ポインタを返す関数ポインタを返す関数ポインタを返す...多分 50 回くらい繰り返し...を返す関数ポインタ」みたいな型で代用
- C にクロージャがない → クロージャを表す関数を 1000 個くらい定義して回避
- 大量の関数定義はサイズ制限に合わない → マクロを駆使して畳み込み
結果、C コンパイラいじめの塊になりました。詳細はドキュメントを見てください。
なお、処理系部分は項書き換えっぽいアプローチで実装してます (コードの形状が示す通り) 。おかげでかなり小さくなっていると思う。
狙いと戦略
これは一番頑張った作品で、ネタにもコンパイラいじめ度にも自信ありました。が、掲載順位から察するに、それほど評価されなかった印象です。地味だからですかね。去年の金賞も地味なんだけどなあ。残念。