Skip to content

Instantly share code, notes, and snippets.

@wweic
Created September 19, 2013 23:08
Show Gist options
  • Save wweic/6631109 to your computer and use it in GitHub Desktop.
Save wweic/6631109 to your computer and use it in GitHub Desktop.
;; A Scheme-to-C compiler.
;; Author: Matthew Might
;; Site: http://matt.might.net/
;; http://www.ucombinator.org/
;; The purpose of this compiler is to demonstrate
;; the most direct possible mapping of Scheme into C.
;; Toward that end, the compiler uses only two
;; intermediate transformations: mutable-variable
;; elimination and closure-conversion.
;; To run the compiler:
;; $ interp this-file.scm < program.scm > out.c
;; $ gcc -o out out.c
;; (It's useful to compare this compiler to the
;; Scheme-to-Java compiler that started from the
;; same codebase.)
;; The compiler handles Core Scheme and some extras.
;; With a macro system like syntax-rules and some
;; more primitives, all of R5RS could be supported.
;; Unlike the Java version, this compiler handles
;; recursion using a lets+sets transformation.
;; The compilation proceeds from Core Scheme plus
;; sugar through three intermediate languages:
;; Core Scheme + Sugar
;; =[desugar]=>
;; Core Scheme
;; =[mutable variable elimination]=>
;; Intermediate Scheme (1)
;; =[closure conversion]=>
;; Intermediate Scheme (2)
;; =[code emission]=>
;; C
;; Core input language:
;; <exp> ::= <const>
;; | <prim>
;; | <var>
;; | (lambda (<var> ...) <exp>)
;; | (if <exp> <exp> <exp>)
;; | (set! <var> <exp>)
;; | (<exp> <exp> ...)
;; <const> ::= <int>
;; | #f
;; Syntactic sugar:
;; <exp> ::+ (let ((<var> <exp>) ...) <exp>)
;; | (letrec ((<var> <exp>) ...) <exp>)
;; | (begin <exp> ...)
;; Intermediate language (1)
;; <exp> ::+ (cell <exp>)
;; | (cell-get <exp>)
;; | (set-cell! <exp> <value>)
;; Intermediate language (2)
;; <exp> ::+ (closure <lambda-exp> <env-exp>)
;; | (env-make <env-num> (<symbol> <exp>) ...)
;; | (env-get <env-num> <symbol> <exp>)
;; Utilities.
; void : -> void
(define (void) (if #f #t))
; tagged-list? : symbol value -> boolean
(define (tagged-list? tag l)
(and (pair? l)
(eq? tag (car l))))
; char->natural : char -> natural
(define (char->natural c)
(let ((i (char->integer c)))
(if (< i 0)
(* -2 i)
(+ (* 2 i) 1))))
; integer->char-list : integer -> string
(define (integer->char-list n)
(string->list (number->string n)))
; gensym-count : integer
(define gensym-count 0)
; gensym : symbol -> symbol
(define gensym (lambda params
(if (null? params)
(begin
(set! gensym-count (+ gensym-count 1))
(string->symbol (string-append
"$"
(number->string gensym-count))))
(begin
(set! gensym-count (+ gensym-count 1))
(string->symbol (string-append
(if (symbol? (car params))
(symbol->string (car params))
(car params))
"$"
(number->string gensym-count)))))))
; member : symbol sorted-set[symbol] -> boolean
(define (member sym S)
(if (not (pair? S))
#f
(if (eq? sym (car S))
#t
(member sym (cdr S)))))
; symbol<? : symbol symobl -> boolean
(define (symbol<? sym1 sym2)
(string<? (symbol->string sym1)
(symbol->string sym2)))
; insert : symbol sorted-set[symbol] -> sorted-set[symbol]
(define (insert sym S)
(if (not (pair? S))
(list sym)
(cond
((eq? sym (car S)) S)
((symbol<? sym (car S)) (cons sym S))
(else (cons (car S) (insert sym (cdr S)))))))
; remove : symbol sorted-set[symbol] -> sorted-set[symbol]
(define (remove sym S)
(if (not (pair? S))
'()
(if (eq? (car S) sym)
(cdr S)
(cons (car S) (remove sym (cdr S))))))
; union : sorted-set[symbol] sorted-set[symbol] -> sorted-set[symbol]
(define (union set1 set2)
; NOTE: This should be implemented as merge for efficiency.
(if (not (pair? set1))
set2
(insert (car set1) (union (cdr set1) set2))))
; difference : sorted-set[symbol] sorted-set[symbol] -> sorted-set[symbol]
(define (difference set1 set2)
; NOTE: This can be similarly optimized.
(if (not (pair? set2))
set1
(difference (remove (car set2) set1) (cdr set2))))
; reduce : (A A -> A) list[A] A -> A
(define (reduce f lst init)
(if (not (pair? lst))
init
(reduce f (cdr lst) (f (car lst) init))))
; azip : list[A] list[B] -> alist[A,B]
(define (azip list1 list2)
(if (and (pair? list1) (pair? list2))
(cons (list (car list1) (car list2))
(azip (cdr list1) (cdr list2)))
'()))
; assq-remove-key : alist[A,B] A -> alist[A,B]
(define (assq-remove-key env key)
(if (not (pair? env))
'()
(if (eq? (car (car env)) key)
(assq-remove-key (cdr env) key)
(cons (car env) (assq-remove-key (cdr env) key)))))
; assq-remove-keys : alist[A,B] list[A] -> alist[A,B]
(define (assq-remove-keys env keys)
(if (not (pair? keys))
env
(assq-remove-keys (assq-remove-key env (car keys)) (cdr keys))))
;; Data type predicates and accessors.
; const? : exp -> boolean
(define (const? exp)
(or (integer? exp)
(boolean? exp)))
; ref? : exp -> boolean
(define (ref? exp)
(symbol? exp))
; let? : exp -> boolean
(define (let? exp)
(tagged-list? 'let exp))
; let->bindings : let-exp -> alist[symbol,exp]
(define (let->bindings exp)
(cadr exp))
; let->exp : let-exp -> exp
(define (let->exp exp)
(caddr exp))
; let->bound-vars : let-exp -> list[symbol]
(define (let->bound-vars exp)
(map car (cadr exp)))
; let->args : let-exp -> list[exp]
(define (let->args exp)
(map cadr (cadr exp)))
; letrec? : exp -> boolean
(define (letrec? exp)
(tagged-list? 'letrec exp))
; letrec->bindings : letrec-exp -> alist[symbol,exp]
(define (letrec->bindings exp)
(cadr exp))
; letrec->exp : letrec-exp -> exp
(define (letrec->exp exp)
(caddr exp))
; letrec->exp : letrec-exp -> list[symbol]
(define (letrec->bound-vars exp)
(map car (cadr exp)))
; letrec->exp : letrec-exp -> list[exp]
(define (letrec->args exp)
(map cadr (cadr exp)))
; lambda? : exp -> boolean
(define (lambda? exp)
(tagged-list? 'lambda exp))
; lambda->formals : lambda-exp -> list[symbol]
(define (lambda->formals exp)
(cadr exp))
; lambda->exp : lambda-exp -> exp
(define (lambda->exp exp)
(caddr exp))
; if? : exp -> boolean
(define (if? exp)
(tagged-list? 'if exp))
; if->condition : if-exp -> exp
(define (if->condition exp)
(cadr exp))
; if->then : if-exp -> exp
(define (if->then exp)
(caddr exp))
; if->else : if-exp -> exp
(define (if->else exp)
(cadddr exp))
; app? : exp -> boolean
(define (app? exp)
(pair? exp))
; app->fun : app-exp -> exp
(define (app->fun exp)
(car exp))
; app->args : app-exp -> list[exp]
(define (app->args exp)
(cdr exp))
; prim? : exp -> boolean
(define (prim? exp)
(or (eq? exp '+)
(eq? exp '-)
(eq? exp '*)
(eq? exp '=)
(eq? exp 'display)))
; begin? : exp -> boolean
(define (begin? exp)
(tagged-list? 'begin exp))
; begin->exps : begin-exp -> list[exp]
(define (begin->exps exp)
(cdr exp))
; set! : exp -> boolean
(define (set!? exp)
(tagged-list? 'set! exp))
; set!->var : set!-exp -> var
(define (set!->var exp)
(cadr exp))
; set!->exp : set!-exp -> exp
(define (set!->exp exp)
(caddr exp))
; closure? : exp -> boolean
(define (closure? exp)
(tagged-list? 'closure exp))
; closure->lam : closure-exp -> exp
(define (closure->lam exp)
(cadr exp))
; closure->env : closure-exp -> exp
(define (closure->env exp)
(caddr exp))
; env-make? : exp -> boolean
(define (env-make? exp)
(tagged-list? 'env-make exp))
; env-make->id : env-make-exp -> env-id
(define (env-make->id exp)
(cadr exp))
; env-make->fields : env-make-exp -> list[symbol]
(define (env-make->fields exp)
(map car (cddr exp)))
; env-make->values : env-make-exp -> list[exp]
(define (env-make->values exp)
(map cadr (cddr exp)))
; env-get? : exp -> boolen
(define (env-get? exp)
(tagged-list? 'env-get exp))
; env-get->id : env-get-exp -> env-id
(define (env-get->id exp)
(cadr exp))
; env-get->field : env-get-exp -> symbol
(define (env-get->field exp)
(caddr exp))
; env-get->env : env-get-exp -> exp
(define (env-get->env exp)
(cadddr exp))
; set-cell!? : set-cell!-exp -> boolean
(define (set-cell!? exp)
(tagged-list? 'set-cell! exp))
; set-cell!->cell : set-cell!-exp -> exp
(define (set-cell!->cell exp)
(cadr exp))
; set-cell!->value : set-cell!-exp -> exp
(define (set-cell!->value exp)
(caddr exp))
; cell? : exp -> boolean
(define (cell? exp)
(tagged-list? 'cell exp))
; cell->value : cell-exp -> exp
(define (cell->value exp)
(cadr exp))
; cell-get? : exp -> boolean
(define (cell-get? exp)
(tagged-list? 'cell-get exp))
; cell-get->cell : cell-exp -> exp
(define (cell-get->cell exp)
(cadr exp))
;; Syntax manipulation.
; substitute-var : alist[var,exp] ref-exp -> exp
(define (substitute-var env var)
(let ((sub (assq var env)))
(if sub
(cadr sub)
var)))
; substitute : alist[var,exp] exp -> exp
(define (substitute env exp)
(define (substitute-with env)
(lambda (exp)
(substitute env exp)))
(cond
; Core forms:
((null? env) exp)
((const? exp) exp)
((prim? exp) exp)
((ref? exp) (substitute-var env exp))
((lambda? exp) `(lambda ,(lambda->formals exp)
,(substitute (assq-remove-keys env (lambda->formals exp))
(lambda->exp exp))))
((set!? exp) `(set! ,(substitute-var env (set!->var exp))
,(substitute env (set!->exp exp))))
((if? exp) `(if ,(substitute env (if->condition exp))
,(substitute env (if->then exp))
,(substitute env (if->else exp))))
; Sugar:
((let? exp) `(let ,(azip (let->bound-vars exp)
(map (substitute-with env) (let->args exp)))
,(substitute (assq-remove-keys env (let->bound-vars exp))
(let->exp exp))))
((letrec? exp) (let ((new-env (assq-remove-keys env (letrec->bound-vars exp))))
`(letrec ,(azip (letrec->bound-vars exp)
(map (substitute-with new-env)
(letrec->args exp)))
,(substitute new-env (letrec->exp exp)))))
((begin? exp) (cons 'begin (map (substitute-with env) (begin->exps exp))))
; IR (1):
((cell? exp) `(cell ,(substitute env (cell->value exp))))
((cell-get? exp) `(cell-get ,(substitute env (cell-get->cell exp))))
((set-cell!? exp) `(set-cell! ,(substitute env (set-cell!->cell exp))
,(substitute env (set-cell!->value exp))))
; IR (2):
((closure? exp) `(closure ,(substitute env (closure->lam exp))
,(substitute env (closure->env exp))))
((env-make? exp) `(env-make ,(env-make->id exp)
,@(azip (env-make->fields exp)
(map (substitute-with env)
(env-make->values exp)))))
((env-get? exp) `(env-get ,(env-get->id exp)
,(env-get->field exp)
,(substitute env (env-get->env exp))))
; Application:
((app? exp) (map (substitute-with env) exp))
(else (error "unhandled expression type in substitution: " exp))))
;; Desugaring.
; let=>lambda : let-exp -> app-exp
(define (let=>lambda exp)
(if (let? exp)
(let ((vars (map car (let->bindings exp)))
(args (map cadr (let->bindings exp))))
`((lambda (,@vars) ,(let->exp exp)) ,@args))
exp))
; letrec=>lets+sets : letrec-exp -> exp
(define (letrec=>lets+sets exp)
(if (letrec? exp)
(let* ((bindings (letrec->bindings exp))
(namings (map (lambda (b) (list (car b) #f)) bindings))
(names (letrec->bound-vars exp))
(sets (map (lambda (binding)
(cons 'set! binding))
bindings))
(args (letrec->args exp)))
`(let ,namings
(begin ,@(append sets (list (letrec->exp exp))))))))
; begin=>let : begin-exp -> let-exp
(define (begin=>let exp)
(define (singlet? l)
(and (list? l)
(= (length l) 1)))
(define (dummy-bind exps)
(cond
((singlet? exps) (car exps))
((pair? exps) `(let (($_ ,(car exps)))
,(dummy-bind (cdr exps))))))
(dummy-bind (begin->exps exp)))
; desugar : exp -> exp
(define (desugar exp)
(cond
; Core forms:
((const? exp) exp)
((prim? exp) exp)
((ref? exp) exp)
((lambda? exp) `(lambda ,(lambda->formals exp)
,(desugar (lambda->exp exp))))
((set!? exp) `(set! ,(set!->var exp) ,(set!->exp exp)))
((if? exp) `(if ,(if->condition exp)
,(if->then exp)
,(if->else exp)))
; Sugar:
((let? exp) (desugar (let=>lambda exp)))
((letrec? exp) (desugar (letrec=>lets+sets exp)))
((begin? exp) (desugar (begin=>let exp)))
; IR (1):
((cell? exp) `(cell ,(desugar (cell->value exp))))
((cell-get? exp) `(cell-get ,(desugar (cell-get->cell exp))))
((set-cell!? exp) `(set-cell! ,(desugar (set-cell!->cell exp))
,(desugar (set-cell!->value exp))))
; IR (2):
((closure? exp) `(closure ,(desugar (closure->lam exp))
,(desugar (closure->env exp))))
((env-make? exp) `(env-make ,(env-make->id exp)
,@(azip (env-make->fields exp)
(map desugar (env-make->values exp)))))
((env-get? exp) `(env-get ,(env-get->id exp)
,(env-get->field exp)
,(env-get->env exp)))
; Applications:
((app? exp) (map desugar exp))
(else (error "unknown exp: " exp))))
;; Syntactic analysis.
; free-vars : exp -> sorted-set[var]
(define (free-vars exp)
(cond
; Core forms:
((const? exp) '())
((prim? exp) '())
((ref? exp) (list exp))
((lambda? exp) (difference (free-vars (lambda->exp exp))
(lambda->formals exp)))
((if? exp) (union (free-vars (if->condition exp))
(union (free-vars (if->then exp))
(free-vars (if->else exp)))))
((set!? exp) (union (list (set!->var exp))
(free-vars (set!->exp exp))))
; Sugar:
((let? exp) (free-vars (let=>lambda exp)))
((letrec? exp) not-handled)
((begin? exp) (reduce union (map free-vars (begin->exps exp)) '()))
; IR (1):
((cell-get? exp) (free-vars (cell-get->cell exp)))
((cell? exp) (free-vars (cell->value exp)))
((set-cell!? exp) (union (free-vars (set-cell!->cell exp))
(free-vars (set-cell!->value exp))))
; IR (2):
((closure? exp) (union (free-vars (closure->lam exp))
(free-vars (closure->env exp))))
((env-make? exp) (reduce union (map free-vars (env-make->values exp)) '()))
((env-get? exp) (free-vars (env-get->env exp)))
; Application:
((app? exp) (reduce union (map free-vars exp) '()))
(else (error "unknown expression: " exp))))
;; Mutable variable analysis and elimination.
;; Mutables variables analysis and elimination happens
;; on a desugared Intermediate Language (1).
;; Mutable variable analysis turns mutable variables
;; into heap-allocated cells:
;; For any mutable variable mvar:
;; (lambda (... mvar ...) body)
;; =>
;; (lambda (... $v ...)
;; (let ((mvar (cell $v)))
;; body))
;; (set! mvar value) => (set-cell! mvar value)
;; mvar => (cell-get mvar)
; mutable-variables : list[symbol]
(define mutable-variables '())
; mark-mutable : symbol -> void
(define (mark-mutable symbol)
(set! mutable-variables (cons symbol mutable-variables)))
; is-mutable? : symbol -> boolean
(define (is-mutable? symbol)
(define (is-in? S)
(if (not (pair? S))
#f
(if (eq? (car S) symbol)
#t
(is-in? (cdr S)))))
(is-in? mutable-variables))
; analyze-mutable-variables : exp -> void
(define (analyze-mutable-variables exp)
(cond
; Core forms:
((const? exp) (void))
((prim? exp) (void))
((ref? exp) (void))
((lambda? exp) (analyze-mutable-variables (lambda->exp exp)))
((set!? exp) (begin (mark-mutable (set!->var exp))
(analyze-mutable-variables (set!->exp exp))))
((if? exp) (begin
(analyze-mutable-variables (if->condition exp))
(analyze-mutable-variables (if->then exp))
(analyze-mutable-variables (if->else exp))))
; Sugar:
((let? exp) (begin
(map analyze-mutable-variables (map cadr (let->bindings exp)))
(analyze-mutable-variables (let->exp exp))))
((letrec? exp) (begin
(map analyze-mutable-variables (map cadr (letrec->bindings exp)))
(analyze-mutable-variables (letrec->exp exp))))
((begin? exp) (begin
(map analyze-mutable-variables (begin->exps exp))
(void)))
; Application:
((app? exp) (begin
(map analyze-mutable-variables exp)
(void)))
(else (error "unknown expression type: " exp))))
; wrap-mutables : exp -> exp
(define (wrap-mutables exp)
(define (wrap-mutable-formals formals body-exp)
(if (not (pair? formals))
body-exp
(if (is-mutable? (car formals))
`(let ((,(car formals) (cell ,(car formals))))
,(wrap-mutable-formals (cdr formals) body-exp))
(wrap-mutable-formals (cdr formals) body-exp))))
(cond
; Core forms:
((const? exp) exp)
((ref? exp) (if (is-mutable? exp)
`(cell-get ,exp)
exp))
((prim? exp) exp)
((lambda? exp) `(lambda ,(lambda->formals exp)
,(wrap-mutable-formals (lambda->formals exp)
(wrap-mutables (lambda->exp exp)))))
((set!? exp) `(set-cell! ,(set!->var exp) ,(wrap-mutables (set!->exp exp))))
((if? exp) `(if ,(wrap-mutables (if->condition exp))
,(wrap-mutables (if->then exp))
,(wrap-mutables (if->else exp))))
; Application:
((app? exp) (map wrap-mutables exp))
(else (error "unknown expression type: " exp))))
;; Name-mangling.
;; We have to "mangle" Scheme identifiers into
;; C-compatible identifiers, because names like
;; foo-bar/baz are not identifiers in C.
; mangle : symbol -> string
(define (mangle symbol)
(define (m chars)
(if (null? chars)
'()
(if (or (and (char-alphabetic? (car chars)) (not (char=? (car chars) #\_)))
(char-numeric? (car chars)))
(cons (car chars) (m (cdr chars)))
(cons #\_ (append (integer->char-list (char->natural (car chars)))
(m (cdr chars)))))))
(list->string (m (string->list (symbol->string symbol)))))
;; Closure-conversion.
;; Closure conversion operates on a desugared
;; Intermediate Language (2). Closure conversion
;; eliminates all of the free variables from every
;; lambda term.
;; The transform is:
;; (lambda (v1 ... vn) body)
;; =>
;; (closure (lambda ($env v1 ... vn)
;; {xi => (env-get $id xi $env)}body)
;; (env-make $id (x1 x1) ... (xn xn)))
;; where x1,...xn are the free variables in the lambda term.
; type env-id = natural
; num-environments : natural
(define num-environments 0)
; environments : alist*[env-id,symbol]
(define environments '())
; allocate-environment : list[symbol] -> env-id
(define (allocate-environment fields)
(let ((id num-environments))
(set! num-environments (+ 1 num-environments))
(set! environments (cons (cons id fields) environments))
id))
; get-environment : natural -> list[symbol]
(define (get-environment id)
(cdr (assv id environments)))
; closure-convert : exp -> exp
(define (closure-convert exp)
(cond
((const? exp) exp)
((prim? exp) exp)
((ref? exp) exp)
((lambda? exp) (let* (($env (gensym 'env))
(body (closure-convert (lambda->exp exp)))
(fv (difference (free-vars body) (lambda->formals exp)))
(id (allocate-environment fv))
(sub (map (lambda (v)
(list v `(env-get ,id ,v ,$env)))
fv)))
`(closure (lambda (,$env ,@(lambda->formals exp))
,(substitute sub body))
(env-make ,id ,@(azip fv fv)))))
((if? exp) `(if ,(closure-convert (if->condition exp))
,(closure-convert (if->then exp))
,(closure-convert (if->else exp))))
((set!? exp) `(set! ,(set!->var exp)
,(closure-convert (set!->exp exp))))
; IR (1):
((cell? exp) `(cell ,(closure-convert (cell->value exp))))
((cell-get? exp) `(cell-get ,(closure-convert (cell-get->cell exp))))
((set-cell!? exp) `(set-cell! ,(closure-convert (set-cell!->cell exp))
,(closure-convert (set-cell!->value exp))))
; Applications:
((app? exp) (map closure-convert exp))
(else (error "unhandled exp: " exp))))
;; Compilation routines.
; c-compile-program : exp -> string
(define (c-compile-program exp)
(let* ((preamble "")
(append-preamble (lambda (s)
(set! preamble (string-append preamble " " s "\n"))))
(body (c-compile-exp exp append-preamble)))
(string-append
"int main (int argc, char* argv[]) {\n"
preamble
" __sum = MakePrimitive(__prim_sum) ;\n"
" __product = MakePrimitive(__prim_product) ;\n"
" __difference = MakePrimitive(__prim_difference) ;\n"
" __display = MakePrimitive(__prim_display) ;\n"
" __numEqual = MakePrimitive(__prim_numEqual) ;\n"
" " body " ;\n"
" return 0;\n"
" }\n")))
; c-compile-exp : exp (string -> void) -> string
(define (c-compile-exp exp append-preamble)
(cond
; Core forms:
((const? exp) (c-compile-const exp))
((prim? exp) (c-compile-prim exp))
((ref? exp) (c-compile-ref exp))
((if? exp) (c-compile-if exp append-preamble))
; IR (1):
((cell? exp) (c-compile-cell exp append-preamble))
((cell-get? exp) (c-compile-cell-get exp append-preamble))
((set-cell!? exp) (c-compile-set-cell! exp append-preamble))
; IR (2):
((closure? exp) (c-compile-closure exp append-preamble))
((env-make? exp) (c-compile-env-make exp append-preamble))
((env-get? exp) (c-compile-env-get exp append-preamble))
; Application:
((app? exp) (c-compile-app exp append-preamble))
(else (error "unknown exp in c-compile-exp: " exp))))
; c-compile-const : const-exp -> string
(define (c-compile-const exp)
(cond
((integer? exp) (string-append
"MakeInt(" (number->string exp) ")"))
((boolean? exp) (string-append
"MakeBoolean(" (if exp "1" "0") ")"))
(else (error "unknown constant: " exp))))
; c-compile-prim : prim-exp -> string
(define (c-compile-prim p)
(cond
((eq? '+ p) "__sum")
((eq? '- p) "__difference")
((eq? '* p) "__product")
((eq? '= p) "__numEqual")
((eq? 'display p) "__display")
(else (error "unhandled primitive: " p))))
; c-compile-ref : ref-exp -> string
(define (c-compile-ref exp)
(mangle exp))
; c-compile-args : list[exp] (string -> void) -> string
(define (c-compile-args args append-preamble)
(if (not (pair? args))
""
(string-append
(c-compile-exp (car args) append-preamble)
(if (pair? (cdr args))
(string-append ", " (c-compile-args (cdr args) append-preamble))
""))))
; c-compile-app : app-exp (string -> void) -> string
(define (c-compile-app exp append-preamble)
(let (($tmp (mangle (gensym 'tmp))))
(append-preamble (string-append
"Value " $tmp " ; "))
(let* ((args (app->args exp))
(fun (app->fun exp)))
(string-append
"(" $tmp " = " (c-compile-exp fun append-preamble)
","
$tmp ".clo.lam("
"MakeEnv(" $tmp ".clo.env)"
(if (null? args) "" ",")
(c-compile-args args append-preamble) "))"))))
; c-compile-if : if-exp -> string
(define (c-compile-if exp append-preamble)
(string-append
"(" (c-compile-exp (if->condition exp) append-preamble) ").b.value ? "
"(" (c-compile-exp (if->then exp) append-preamble) ") : "
"(" (c-compile-exp (if->else exp) append-preamble) ")"))
; c-compile-set-cell! : set-cell!-exp (string -> void) -> string
(define (c-compile-set-cell! exp append-preamble)
(string-append
"(*"
"(" (c-compile-exp (set-cell!->cell exp) append-preamble) ".cell.addr)" " = "
(c-compile-exp (set-cell!->value exp) append-preamble)
")"))
; c-compile-cell-get : cell-get-exp (string -> void) -> string
(define (c-compile-cell-get exp append-preamble)
(string-append
"(*("
(c-compile-exp (cell-get->cell exp) append-preamble)
".cell.addr"
"))"))
; c-compile-cell : cell-exp (string -> void) -> string
(define (c-compile-cell exp append-preamble)
(string-append
"NewCell(" (c-compile-exp (cell->value exp) append-preamble) ")"))
; c-compile-env-make : env-make-exp (string -> void) -> string
(define (c-compile-env-make exp append-preamble)
(string-append
"MakeEnv(__alloc_env" (number->string (env-make->id exp))
"("
(c-compile-args (env-make->values exp) append-preamble)
"))"))
; c-compile-env-get : env-get (string -> void) -> string
(define (c-compile-env-get exp append-preamble)
(string-append
"((struct __env_"
(number->string (env-get->id exp)) "*)"
(c-compile-exp (env-get->env exp) append-preamble) ".env.env)->"
(mangle (env-get->field exp))))
;; Lambda compilation.
;; Lambdas get compiled into procedures that,
;; once given a C name, produce a C function
;; definition with that name.
;; These procedures are stored up an eventually
;; emitted.
; type lambda-id = natural
; num-lambdas : natural
(define num-lambdas 0)
; lambdas : alist[lambda-id,string -> string]
(define lambdas '())
; allocate-lambda : (string -> string) -> lambda-id
(define (allocate-lambda lam)
(let ((id num-lambdas))
(set! num-lambdas (+ 1 num-lambdas))
(set! lambdas (cons (list id lam) lambdas))
id))
; get-lambda : lambda-id -> (symbol -> string)
(define (get-lambda id)
(cdr (assv id lambdas)))
; c-compile-closure : closure-exp (string -> void) -> string
(define (c-compile-closure exp append-preamble)
(let* ((lam (closure->lam exp))
(env (closure->env exp))
(lid (allocate-lambda (c-compile-lambda lam))))
(string-append
"MakeClosure("
"__lambda_" (number->string lid)
","
(c-compile-exp env append-preamble)
")")))
; c-compile-formals : list[symbol] -> string
(define (c-compile-formals formals)
(if (not (pair? formals))
""
(string-append
"Value "
(mangle (car formals))
(if (pair? (cdr formals))
(string-append ", " (c-compile-formals (cdr formals)))
""))))
; c-compile-lambda : lamda-exp (string -> void) -> (string -> string)
(define (c-compile-lambda exp)
(let* ((preamble "")
(append-preamble (lambda (s)
(set! preamble (string-append preamble " " s "\n")))))
(let ((formals (c-compile-formals (lambda->formals exp)))
(body (c-compile-exp (lambda->exp exp) append-preamble)))
(lambda (name)
(string-append "Value " name "(" formals ") {\n"
preamble
" return " body " ;\n"
"}\n")))))
; c-compile-env-struct : list[symbol] -> string
(define (c-compile-env-struct env)
(let* ((id (car env))
(fields (cdr env))
(sid (number->string id))
(tyname (string-append "struct __env_" sid)))
(string-append
"struct __env_" (number->string id) " {\n"
(apply string-append (map (lambda (f)
(string-append
" Value "
(mangle f)
" ; \n"))
fields))
"} ;\n\n"
tyname "*" " __alloc_env" sid
"(" (c-compile-formals fields) ")" "{\n"
" " tyname "*" " t = malloc(sizeof(" tyname "))" ";\n"
(apply string-append
(map (lambda (f)
(string-append " t->" (mangle f) " = " (mangle f) ";\n"))
fields))
" return t;\n"
"}\n\n"
)))
;; Code emission.
(define (emit line)
(display line)
(newline))
; c-compile-and-emit : (string -> A) exp -> void
(define (c-compile-and-emit emit input-program)
(define compiled-program "")
(set! input-program (desugar input-program))
(analyze-mutable-variables input-program)
(set! input-program (desugar (wrap-mutables input-program)))
(set! input-program (closure-convert input-program))
(emit "#include <stdlib.h>")
(emit "#include <stdio.h>")
(emit "#include \"scheme.h\"")
(emit "")
; Create storage for primitives:
(emit "
Value __sum ;
Value __difference ;
Value __product ;
Value __display ;
Value __numEqual ;
")
(for-each
(lambda (env)
(emit (c-compile-env-struct env)))
environments)
(set! compiled-program (c-compile-program input-program))
;; Emit primitive procedures:
(emit
"Value __prim_sum(Value e, Value a, Value b) {
return MakeInt(a.z.value + b.z.value) ;
}")
(emit
"Value __prim_product(Value e, Value a, Value b) {
return MakeInt(a.z.value * b.z.value) ;
}")
(emit
"Value __prim_difference(Value e, Value a, Value b) {
return MakeInt(a.z.value - b.z.value) ;
}")
(emit
"Value __prim_display(Value e, Value v) {
printf(\"%i\\n\",v.z.value) ;
return v ;
}")
(emit
"Value __prim_numEqual(Value e, Value a, Value b) {
return MakeBoolean(a.z.value == b.z.value) ;
}")
;; Emit lambdas:
; Print the prototypes:
(for-each
(lambda (l)
(emit (string-append "Value __lambda_" (number->string (car l)) "() ;")))
lambdas)
(emit "")
; Print the definitions:
(for-each
(lambda (l)
(emit ((cadr l) (string-append "__lambda_" (number->string (car l))))))
lambdas)
(emit compiled-program))
;; Compile and emit:
(define the-program (read))
(c-compile-and-emit emit the-program)
; Suitable definitions for the cell functions:
;(define (cell value) (lambda (get? new-value)
; (if get? value (set! value new-value))))
;(define (set-cell! c v) (c #f v))
;(define (cell-get c) (c #t #t))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment