\$\begingroup\$

Newest:

I've squeezed it down to 644 chars, I factored parts of cEll into cOpy and Par; cached calls to cell and cdr into temporary local variables, and moved those local variables to globals in "terminal" (ie. non-recursive) functions. Also, decimal constants are shorter than character literals and this nasty business ...

atom(x){ return m[x]>>5==3; }

... correctly identifies lowercase letters (assuming ASCII), but also accepts any of `{|}~. (This same observation about ASCII is made in this excellent video about UTF-8.)

Et viola:|

#include<stdio.h> #include<string.h> #define X m[x] #define R return char*n,*m;int u,w,d;C(x,y){w=n-m;n+=sprintf(n,y?"(%s %s)":"(%s)",&X,m+y)+1;R w;}T(x){R X>>5==3;} L(x){R X==92;}O(x,j){w=n-m;memcpy(n,&X,j);n+=j;*n++=0;R w;}E(x){X==' '?++x:0;R X==41?0:L(x)?O(x,4):P(x);}P(x){d=0,w=x;do{X==40?d++:X==41?d--:0;++x;}while(d>0);R O(w,x-w);}D(x){u=E(x+1);R u?E(x+1+strlen(m+u)):0;}V(x){int a=E(x+1),b=D(x);R T(x)|T(a)?x:L(a)?C(a,V(b)):L(E(a+1))?V(S(V(b),E(a+3),D(a))):V(C(V(a),b?V(b):0));}S(w,y,x){R T(x)?(X==m[y]?w:x):C(L(w+1)?E(x+1):S(w,y,E(x+1)),D(x)?S(w,y,D(x)):0);} Y(char*s){n+=strlen(s=strcpy(n,s))+1;printf("%s

%s



",s,m+V(s-m));n=m+1;} char*s[]={ "((\\ a. a) (b))", "((\\ x. x) (\\ y. (\\ z. z)))", "(\\ x. ((\\ y. y) x))", "(((\\ x. (\\ y. x)) (\\ a. a)) (\\ b. b))", "((\\ x. (\\ y. y)) (\\ a. a))", "(((\\ x. (\\ y. y)) (\\ a. a)) (\\ b. b))", "((\\x. (x x)) (\\x. (x x)))",0}; #include<unistd.h> main(){char**k;n=m=sbrk(4096);*n++=0;for(k=s;*k;k++)Y(*k);R 0;}

Earlier:

Can I get a few votes for effort? I've been working on this day and night for a week. I dug out the original McCarthy paper and was plagued by a bug in the paper itself until I read the appendix to Paul Graham's The Roots of Lisp. I was so distracted that I locked myself out of my house, then completely forgot until arriving home again that night at 12:30 (a little late to be calling the building manager who lives way out in the county), and had to spend the night at my grandmother's (hacking away until my laptop battery was dry).

And after all that, it's not even close to the winning entry!

I'm not sure how to make this any shorter; and I've used all the dirty tricks I can think of! Maybe it can't be done in C.

With some generosity in the counting (the first chunk takes a string and prints out the result), it's 778 770 709 694 chars. But to make it stand-alone, it has to have that sbrk call. And to handle more complicated expressions, it needs the signal handler, too. And of course it cannot be made into a module with any code that tries to use malloc .

So, alas, here it is:

#include<stdio.h> #include<string.h> #define K(j) strncpy(n,m+x,j);n+=j;goto N; #define R return #define X m[x] #define L =='\\' char*m,*n;T(x){R islower(X);}V(x){int a=E(x+1);R T(x)?x:T(a)?x:m[a]L?C(a,V(D(x))):m[E(a+1)]L?V(S(V(D(x)),E(a+3),D(a))):V(C(V(a),D(x)?V(D(x)):0));} C(x,y){char*t=n;sprintf(n,y?"(%s %s)":"(%s)",m+x,m+y);n+=strlen(n)+1;R t-m;}Y(char*s){char*t=strcpy(n,s);n+=strlen(n)+1;printf("%s=>%s

",s,m+V(t-m));n=m+1;}S(x,y,z){R T(z)?(m[z]==m[y]?x:z):C(m[z+1]L?E(z+1):S(x,y,E(z+1)),D(z)?S(x,y,D(z)):0);}D(x){R E(x+1)?E(x+strlen(m+E(x+1))+1):0;}E(x){char*t=n,d=0;if(X==' ')++x;if(T(x)){K(1)}if(X L){K(4)}do{d=X?(X=='('?d+1:(X==')'?d-1:d)):0;*n++=m[x++];}while(d);N:*n++=0;R t-m;} char*samp[]={ "a","a","b","b", "((\\ a. a) (b))", "(b)", "((\\ x. x) (\\ y. (\\ z. z)))", "(\\ y. (\\ z. z))", "(\\ x. ((\\ y. y) x))", "(\\ x. x)", "(((\\ x. (\\ y. x)) (\\ a. a)) (\\ b. b))", "(\\ a. a)", "((\\ x. (\\ y. y)) (\\ a. a))", "(\\ y. y)", "(((\\ x. (\\ y. y)) (\\ a. a)) (\\ b. b))", "(\\ b. b)", "((\\x. (x x)) (\\x. (x x)))", "undef", NULL}; #include<unistd.h> unsigned sz; #include<signal.h> void fix(x){signal(SIGSEGV,fix);brk(m+(sz*=2));} main(){ char**t; signal(SIGSEGV,fix); m=n=sbrk(sz=10*getpagesize()); *n++=0; for(t=samp;*t;t+=2){ Y(*t); printf("s.b. => %s



", t[1]); } return 0; }

Here's the block just before the final reductions. The tricks here are integer cursors instead of pointers (taking advantage of the 'implicit int' behavior), and the use of 'scratch memory': the char*n is the 'new' or 'next' pointer into the free space. But sometimes I write a string into the memory, then call strlen and increment n; effectively using memory and then allocating it, after the size is easier to calculate. You can see it's pretty much straight from the McCarthy paper, with the exception of cell() which interfaces between the functions and the string representation of data.