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 コンパイラいじめの塊になりました。詳細はドキュメントを見てください。
なお、処理系部分は項書き換えっぽいアプローチで実装してます (コードの形状が示す通り) 。おかげでかなり小さくなっていると思う。
狙いと戦略
これは一番頑張った作品で、ネタにもコンパイラいじめ度にも自信ありました。が、掲載順位から察するに、それほど評価されなかった印象です。地味だからですかね。去年の金賞も地味なんだけどなあ。残念。