

Implement a programming language from scratch - sea6ear
http://matt.might.net/articles/implementing-a-programming-language/

======
bishc
In my experience, many people will miss the significance of this, or will
simply fail to grasp how this is implementing a programming language at all.
After all, you're taking advantage of the host language for every single
aspect of the new language, even tokenizing/parsing.

More interesting for beginners (in my opinion) are examples implementing a
simple tokenizer, parser, and compiler to another source language. This seems
to be what closes the gap between programming languages as mystical constructs
and programming languages as programs themselves.

This point of view does tend to displease the traditional SICP crowd though
:-).

~~~
joe_the_user
Here is a prolog in interpreter in c++.

[http://www.cl.cam.ac.uk/~am21/research/funnel/prolog.c](http://www.cl.cam.ac.uk/~am21/research/funnel/prolog.c)

A list of tiny, powerful interpreters would be good.

~~~
tromp
The (binary) lambda calculus interpreter at
[http://www.ioccc.org/2012/tromp/tromp.c](http://www.ioccc.org/2012/tromp/tromp.c),
documented in
[http://www.ioccc.org/2012/tromp/hint.html](http://www.ioccc.org/2012/tromp/hint.html)
is as tiny (and incomprehensible) as it gets, but does all of input, recursive
descent parsing, translation to bytecode, lazy evaluation (call-by-need to be
precise), garbage collection, and output.

~~~
joe_the_user
Would it be possible to simply a have a normally formatted version of the
code?

~~~
tromp
A slightly less obfuscated and somewhat more normally formatted version:

    
    
      #include <stdio.h>
      #include <stdlib.h>
      #include <assert.h>
      enum {I,O,V,A,L};
      int n=44,i,c,T[M]={L,A,8,A,2,  V,0,L,L,V,
          A,30,L,A,2,V,0,L,A,5,A,7,L,V,0,O,
          A,14,L,A,2,V,0,L,A,5,A,2,  V,0,O,O,A},b,s;
      typedef struct _{int t,r; struct _*e,*n;} C;C*e,*f,*l,*S[M];
      void x(int l,int u){for(;l<=u;T[n++]=T[l++]);}
      int g(){i--||(i=b,c=getchar());return c>>i&1;}
      void d(C*l){!l||--l->r||(d(l->e),d(l->n),l->n=f,f=l);}
      int p(int m){if(g()){for(T[n++]=V;g();T[n]++);n++;}else
                T[m]=n++&&g()?(T[m+1]=p(++n),A):L,p(n);return n-m;}
      int main(int t){char o;
       b=t>1?0:7;T[43]=p(n);i=0;
       for(t=b?10:26;;)switch(T[t]){
        case I: g();i++;assert(n<M-99);if(~c&&b){x(0,6);for(T[n-5]=96;i;T[n++]=!g())
                 x(0,9);}x(c<0?7:b,9);T[n++]=!b&&!g();break;
        case O: t=b+t>42?(o=2*o|t&1,28):(putchar
                    (b?o:t+8),fflush(stdout),b?12:28);break;
        case V: l=e;for(t=T[t+1];t--;e=e->n);
                         t=e->t;(e=e->e)&&e->r++;d(l);break;
        case A: t+=2;f||(f=calloc(1,sizeof(C)));assert(f&&  s<M);S[s++]=l=f;f=l->n;
                l->r=1;l->t=t+T[t-1];(l->e=e)&&e->r++;break;
        case L: if(!s--)return 0;S[s]->n=e;e=S[s];t++;break;
       }
       return T[t+2];
      }

------
samatman
C'mon, why stop there? Implement Scheme in your toy lambda. Then implement
another toy lambda in your Scheme, and a Scheme in that. Continue until it
takes several minutes to do (fib 5). Then, write JIT compiler for that Scheme.
See what it takes to get down to pre-Inception levels of performance.

Now, you've actually learned something: Abstractions are costly.

~~~
gus_massa
Too late, someone already did that and measured the overhead of up to five
interpretations layers: "Scheme benchmarking with a meta-circular" interpreter
[http://yinwang0.wordpress.com/2013/11/04/scheme-
benchmarking...](http://yinwang0.wordpress.com/2013/11/04/scheme-
benchmarking/) Archive:
[http://web.archive.org/web/20140105145428/https://yinwang0.w...](http://web.archive.org/web/20140105145428/https://yinwang0.wordpress.com/2013/11/04/scheme-
benchmarking/) HN discussion:
[https://news.ycombinator.com/item?id=7045734](https://news.ycombinator.com/item?id=7045734)
(25 points, 443 days ago, 9 comments) (The main critic is that it should
compare more scheme implementations.)

Many modern schemes (and similar languages like Racket and Clojure) have many
optimization, use bytecode and jit compiling, so the abstraction overhead is
not as big as in the naive versions. And you get nice code and powerful macros
in exchange.

IIRC Stalin is faster than C in some benchmarks, but the compiling time is
very big. "Stalin: a global optimizing compiler for Scheme"
[https://github.com/barak/stalin](https://github.com/barak/stalin) HN
discussion:
[https://news.ycombinator.com/item?id=8214343](https://news.ycombinator.com/item?id=8214343)
(85 points, 220 days ago, 70 comments)

------
amatic
Here is something from my todo list, build lisp in C:
[http://www.buildyourownlisp.com/](http://www.buildyourownlisp.com/)

------
wesleyy
Scared me for a second, thought he as going to make a programming language in
the MIT gui language "Scratch"

------
blt
Just add garbage collection!

