LC-Language Source Code

From the book Compiling Lambda Calculus

Provided under the Creative Commons Zero (CC0) licence

You might prefer to download the source code archive

Compiler

; LC-language -> C compiler
; By Nils M Holm, 2016
;
; Provided under the Creative Common Zero (CC0) license
; (https://creativecommons.org/publicdomain/zero/1.0/)
;
; From the book COMPILING LAMBDA CALCULUS, http://t3x.org/clc/

(define prim1
  '(car cdr pairp nullp))

(define prim2
  '(cons))

Expansion

(define (expand x)

  (define (x-lambda x)
    (let* ((t (cddr x))
           (t (if (null? (cdr t))
                  (exp (car t))
                  (exp `(seq ,@t)))))
      (if (pair? (cadr x))
          (if (null? (cdadr x))
              `(lam ,(caadr x)
                    ,t)
              `(lam ,(caadr x)
                    ,(x-lambda
                       `(lam ,(cdadr x)
                        ,t))))
          `(lam ,(cadr x) ,t))))

  (define (x-app x)
    (if (or (null? (cdr x))
            (null? (cddr x)))
        x
        (x-app `((,(car x) ,(cadr x))
               ,@(cddr x)))))

  (define (newsym)
    (gensym "%"))

  (define (exp-prim x)
    (cond ((memq x prim1)
            (let ((v (newsym)))
              `(lam ,v (,x ,v))))
          ((memq x prim2)
            (let ((v1 (newsym))
                  (v2 (newsym)))
              (exp `(lam (,v1 ,v2)
                      (,x ,v1 ,v2)))))
          (else
            x)))

  (define (lift-prim x)
    (if (and (not (null? (cdr x)))
             (not (null? (cddr x))))
        x
        (let ((v (newsym)))
          `(lam ,v
             (,(car x) ,(cadr x) ,v)))))

  (define (exp x)
    (cond ((not (pair? x))
            (exp-prim x))
          ((memq (car x) prim1)
            `(,(car x) ,(exp (cadr x))))
          ((memq (car x) prim2)
            (lift-prim x))
          ((memq (car x) '(seq set if))
            (map exp x))
          ((eq? 'id (car x))
            x)
          ((eq? 'lam (car x))
            (x-lambda x))
          (else
            (x-app (map exp x)))))

  (exp x))

Closure Conversion

(define (cconv x)

  (define (bind x e n)
    (cons (cons x n) e))

  (define (cc x e n)
    (cond ((assq x e)
            => (lambda (b)
                 `(%ref ,(cdr b))))
          ((not (pair? x))
            x)
          ((eq? 'id (car x))
            x)
          ((eq? 'lam (car x))
            `(lam ,n ,(cc (caddr x)
                          (bind (cadr x) e n)
                          (+ 1 n))))
          (else
            (cons (cc (car x) e n)
                  (cc (cdr x) e n)))))
  (cc x '() 0))

Tail Call Conversion

(define (but-last x)
  (reverse (cdr (reverse x))))

(define (last x)
  (car (reverse x)))

(define (tconv x)
  (define (tc x t)
    (cond ((not (pair? x))
            x)
          ((memq (car x) '(id %ref))
            x)
          ((eq? (car x) 'lam)
            `(lam ,(cadr x)
                  ,(tc (caddr x) #t)))
          ((eq? (car x) 'set)
            `(set ,(cadr x)
                  ,(tc (caddr x) #f)))
          ((eq? (car x) 'seq)
            `(seq ,@(map (lambda (x)
                           (tc x #f))
                         (but-last (cdr x)))
                  ,(tc (last x) t)))
          ((eq? (car x) 'if)
            `(if ,(tc (cadr x) #f)
                 ,(tc (caddr x) t)
                 ,(tc (cadddr x) t)))
          (else
            `(,(if t '%tail-apply '%apply)
              ,@(map (lambda (x)
                       (tc x #f))
                     x)))))
  (tc x #t))

Code Generation

(define (gen x)
  (define n 1)
  (define lp '(t ()))
  (define a 0)

  (define (lit x)
    (if (null? x)
        0
        (begin (set! lp (cons x lp))
               (set! n (+ 1 n))
               n)))

  (define (addr)
    (set! a (+ 1 a))
    a)

  (define (emit* . x)
    (for-each display x))

  (define (emit . x)
    (apply emit* x)
    (newline))

  (define (gjump x)
    (emit "K = " x "; break;"))

  (define (gjumpf x)
    (emit* "if (NIL == vector(S)[--P]) { ")
    (emit* "K = " x "; break; ")
    (emit "}"))

  (define (glab x)
    (emit "case " x ":"))

  (define (genv x)
    (emit "push(vector(E)[" x "]);")
    (emit* "T = box(vector(S)[P-4]); ")
    (emit "vector(E)[" x "] = T;"))

  (define (glambda x)
    (let ((end (addr))
          (fun (addr)))
      (gjump end)
      (glab fun)
      (genv (cadr x))
      (g (caddr x))
      (emit "K = ret(" (cadr x) "); break;")
      (glab end)
      (emit "pushfun(" fun ", " (cadr x) ");")))

  (define (gset x)
    (let ((a (cadadr x)))
      (g (caddr x))
      (emit "car(vector(E)[" a "]) = vector(S)[P-1];")))

  (define (gseq x)
    (for-each (lambda (x)
                (g x)
                (emit "P--;"))
              (but-last (cdr x)))
    (g (last x)))

  (define (gif x)
    (let ((a (addr))
          (c (addr)))
      (g (cadr x))
      (gjumpf a)
      (g (caddr x))
      (gjump c)
      (glab a)
      (g (cadddr x))
      (glab c)))

  (define (gapp x)
    (let ((a (addr)))
      (g (caddr x))
      (g (cadr x))
      (emit "K = apply("
            a
            ", "
            (if (eq? '%apply (car x)) 0 1)
            "); break;")
      (glab a)))

  (define (gprim1 x)
    (g (caddr x))
    (emit* "T = P_" (cadr x) "(vector(S)[P-1]); ")
    (emit "vector(S)[P-1] = T;"))

  (define (gprim2 x)
    (g (caddr x))
    (g (cadddr x))
    (emit* "T = P_"
           (cadr x)
           "(vector(S)[P-2], "
           "vector(S)[P-1]); ")
    (emit* "vector(S)[P-2] = T; ")
    (emit "P--;"))

  (define (prelude)
    (emit "#include \"lc.c\"")
    (emit "void run(void) {")
    (emit "for (K = 0;;) switch (K) {")
    (emit "case 0:"))

  (define (postlude)
    (emit "printexp(vector(S)[--P]);")
    (emit "return;")
    (emit "}}")
    (emit "int main(void) {")
    (emit "init();")
    (emit "L = mklitpool(\"" (reverse lp) "\");")
    (emit "run();")
    (emit "exit(0);")
    (emit "}"))

  (define (g x)
    (cond ((not (pair? x))
            (emit "#error: L: undefined: " x)
            (error "undefined" x))
          ((eq? (car x) 'id)
            (emit "push(vector(L)[" (lit (cadr x)) "]);"))
          ((eq? (car x) '%ref)
            (emit "push(unbox(vector(E)[" (cadr x) "]));"))
          ((eq? (car x) 'lam)
            (glambda x))
          ((eq? (car x) 'set)
            (gset x))
          ((eq? (car x) 'seq)
            (gseq x))
          ((eq? (car x) 'if)
            (gif x))
          ((memq (cadr x) prim1)
            (gprim1 x))
          ((memq (cadr x) prim2)
            (gprim2 x))
          (else
            (gapp x))))

  (prelude)
  (g x)
  (postlude))

(define (comp x)
  (gen (tconv (cconv (expand x)))))

Runtime Support

/*
 * LC-language -> C compiler, runtime
 * By Nils M Holm, 2016
 *
 * Provided under the Creative Common Zero (CC0) license
 * (https://creativecommons.org/publicdomain/zero/1.0/)
 *
 * From the book COMPILING LAMBDA CALCULUS, http://t3x.org/clc/
 */

#include "s9core.h"
#include "s9import.h"

#define	SEG	1000

#define box(x)	cons((x),NIL)
#define unbox	car

cell	E, L, S, T;
int	K, P, Z;
char	*I;

cell	*R[] = { &E, &L, &S, &T, NULL };

void err(char *s) {
	fprintf(stderr, "L error: %s\n", s);
	exit(1);
}

void init(void) {
	s9_init(R);
	P = 0;
	Z = SEG;
	S = make_vector(Z);
}

cell readexp(void) {
	#define B 256
	char	b[B];
	int	i;
	cell	n, a, p;

	while (isspace(*I))
		I++;
	if ('(' == *I) {
		I++;
		a = NIL;
		n = readexp();
		while (n != VOID) {
			if (NIL == a) {
				save(a = cons(NIL, NIL));
			}
			else {
				p = cons(NIL, NIL);
				cdr(a) = p;
				a = cdr(a);
			}
			car(a) = n;
			n = readexp();
		}
		if (NIL == a)
			return NIL;
		return unsave(1);
	}
	else if (')' == *I) {
		I++;
		return VOID;
	}
	else {
		for (i = 0; !isspace(*I) && ')' != *I; i++) {
			b[i] = *I++;
			if (i >= B) err("symbol too long");
		}
		b[i] = 0;
		return symbol_ref(b);
	}
}

void px(cell x) {
	if (function_p(x)) {
		printf("#<function %d>", (int) cadr(x));
	}
	else if (symbol_p(x)) {
		printf("%s", symbol_name(x));
	}
	else if (NIL == x) {
		printf("()");
	}
	else {
		putchar('(');
		while (!atom_p(x)) {
			px(car(x));
			x = cdr(x);
			if (pair_p(x))
				putchar(' ');
		}
		if (x != NIL) {
			printf(" . ");
			px(x);
		}
		putchar(')');
	}
}

void printexp(cell x) {
	px(x);
	putchar('\n');
}

cell mklitpool(char *s) {
	cell	n, v, *vv;
	int	i;

	I = s;
	n = readexp();
	save(n);
	v = make_vector(length(n));
	unsave(1);
	vv = vector(v);
	for (i = 0; n != NIL; n = cdr(n))
		vv[i++] = car(n);
	return v;
}

void push(cell x) {
	cell	v;

	if (P >= Z) {
		T = x;
		v = make_vector(Z+SEG);
		memcpy(vector(v), vector(S), Z*sizeof(cell));
		Z += SEG;
		S = v;
	}
	vector(S)[P++] = x;
}

void pushfun(int k, int n) {
	cell	fn, ne;

	ne = make_vector(n+1);
	memcpy(vector(ne), vector(E), n*sizeof(cell));
	fn = new_atom(k, ne);
	push(new_atom(T_FUNCTION, fn));
}

int apply(int k, int tco) {
	T = vector(S)[P-1];
	P--;
	if (!function_p(T))
		err("application of non-function");
	if (tco && P > 4) {
		vector(S)[P-5] = vector(S)[P-1];
		P-=2;
	}
	else {
		push(E);
		push(new_atom(k, NIL));
	}
	E = cddr(T);
	return cadr(T);
}

int ret(int n) {
	int	k;

	T = vector(S)[P-1];
	vector(E)[n] = vector(S)[P-2];
	k = car(vector(S)[P-3]);
	E = vector(S)[P-4];
	P -= 4;
	vector(S)[P-1] = T;
	return k;
}

#define P_cons		cons
#define P_car		car
#define P_cdr		cdr
#define P_pairp(x)	(pair_p(x)? vector(L)[1]: NIL)
#define P_nullp(x)	(NIL == x? vector(L)[1]: NIL)

contact