From a89d6c82e981fec7d6e4c975e083d2b9e04467ad Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Mon, 1 May 2023 07:56:42 -0700 Subject: Rewrite most of the compiler. This represents a major step back in terms of functionality, and amount of code. The latter I think constitutes a major win. Next steps are to reimplement syntax-rules, call/cc, and call-with-values. --- lib/csc/macros.scheme | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 lib/csc/macros.scheme (limited to 'lib/csc/macros.scheme') diff --git a/lib/csc/macros.scheme b/lib/csc/macros.scheme new file mode 100644 index 0000000..2d6b790 --- /dev/null +++ b/lib/csc/macros.scheme @@ -0,0 +1,346 @@ +; https://web.cs.ucdavis.edu/~devanbu/teaching/260/kohlbecker.pdf + +(define-library (csc macros) + (export + expand-program + list-builtins) + (import (scheme base) + (only (scheme case-lambda) case-lambda) + (prefix (csc gensym) gensym.) + (prefix (csc ir) ir.) + (prefix (csc list) list.) + (prefix (csc map) map.)) + (import (scheme write)) + (begin + + + ; Macro expansion is a function on an stree, to a simplified form called + ; the lambda-term. An stree is one of: + ; - symbol + ; - constant (null, boolean, char, bytevector, number, string, or vector) + ; - list of stree (function call or macro application) + ; + ; A lambda-term is one of: + ; - symbol + ; - constant + ; - list of stree (only function call this time) + ; - or one of a number of special forms defined in ir.scheme. + ; + ; A lambda-term is the same as an stree, except there are no remaining + ; macro applications. In other words, we are expanding the macros. + + + (define-record-type + (make-toplevel-define var body) + toplevel-define? + (var toplevel-define-var) + (body toplevel-define-body)) + + + (define-record-type + (make-tsvar var timestamp) + tsvar? + (var tsvar-var) + (timestamp tsvar-ts)) + + + (define (stamper ts) + (lambda (var) + (make-tsvar var ts))) + + + (define (constant? x) + (or (boolean? x) + (char? x) + (bytevector? x) + (number? x) + (string? x) + (vector? x))) + + + ; timestamp stamps all unstamped symbols. + (define (timestamp tree stamp) + (cond + ((ir.libvar? tree) + (stamp tree)) + ((list? tree) + (map (lambda (x) (timestamp x stamp)) tree)) + ((constant? tree) tree) + (else (error "unexpected form in timestamp" tree)))) + + + (define (lookup-macro env var) + (define binding1 (map.lookup env var #f)) + (if (procedure? binding1) + binding1 + ; Check for var in the global namespace. + (let ((binding2 (map.lookup env (make-tsvar (tsvar-var var) 0) #f))) + (and (procedure? binding2) + binding2)))) + + + ; expand expands all macros in a timestamped stree. + (define (expand tree env time) + (cond + ((and (pair? tree) + (tsvar? (car tree)) + (lookup-macro env (car tree))) => + (lambda (transformer) + (transformer tree env time))) + ((null? tree) + (error "nil by itself is an error")) + ((list? tree) + (ir.make-apply + (expand (car tree) env time) + (list + (list.foldr + (lambda (x acc) + (ir.make-call-builtin 'cons + (list + (expand x env time) + acc))) + (ir.make-const '()) + (cdr tree))))) + ((tsvar? tree) + (or (map.lookup env tree #f) + tree)) ; Global variables are still unresolved. + ((constant? tree) + (ir.make-const tree)) + (else (error "unexpected form in expand" tree)))) + + + (define (map-ir1 f expr) + (cond + ((toplevel-define? expr) + (make-toplevel-define (f (toplevel-define-var expr)) (f (toplevel-define-body expr)))) + ((ir.lambda? expr) + (ir.make-lambda (map f (ir.lambda-vars expr)) (f (ir.lambda-body expr)))) + ((ir.letrec? expr) + (ir.make-letrec (map (lambda (func) (cons (f (car func)) (f (cdr func)))) (ir.letrec-funcs expr)) (f (ir.letrec-body expr)))) + ((ir.apply? expr) + (ir.make-apply (f (ir.apply-func expr)) (map f (ir.apply-args expr)))) + ((ir.sequence? expr) + (ir.make-sequence (f (ir.sequence-head expr)) (f (ir.sequence-tail expr)))) + ((ir.set? expr) + (ir.make-set (f (ir.set-var expr)) (f (ir.set-body expr)))) + ((ir.call-builtin? expr) + (ir.make-call-builtin (ir.call-builtin-name expr) (map f (ir.call-builtin-args expr)))) + ((or (tsvar? expr) + (gensym.gensym? expr) + (ir.const? expr)) + expr) + ((ir.void? expr) ir.*void*) + (else (error "unexpected form in map-ir1")))) + + + (define (fold-ir1 f acc expr) + (cond + ((toplevel-define? expr) + (f (f acc (toplevel-define-var expr)) (toplevel-define-body expr))) + ((ir.lambda? expr) + (f (list.foldl f acc (ir.lambda-vars expr)) (ir.lambda-body expr))) + ((ir.letrec? expr) + (f (list.foldl (lambda (acc x) (f (f acc (car x)) (cdr x))) acc (ir.letrec-funcs expr)) (ir.letrec-body expr))) + ((ir.apply? expr) + (list.foldl f (f acc (ir.apply-func expr)) (ir.apply-args expr))) + ((ir.sequence? expr) + (f (f acc (ir.sequence-head expr)) (ir.sequence-tail expr))) + ((ir.set? expr) + (f (f acc (ir.set-var expr)) (ir.set-body expr))) + ((ir.call-builtin? expr) + (list.foldl f acc (ir.call-builtin-args expr))) + ((or (tsvar? expr) + (gensym.gensym? expr) + (ir.const? expr) + (ir.void? expr)) + acc) + (else (error "unexpected form in fold-ir1" expr)))) + + + (define (cmp-strings s1 s2) + (cond + ((string=? s1 s2) + 0) + ((string= (length expr) 3) + (tsvar? (cadr expr))) + (error "invalid form in builtin-lambda")) + (let ((var (cadr expr)) + (body (cddr expr)) + (new-var (gensym.gen))) + (define env* (map.insert env var new-var)) + (define expanded-body (expand-body body env* (+ 1 time))) + (define exprs + (let loop ((body expanded-body)) + (if (or (null? body) + (not (toplevel-define? (car body)))) + body + (loop (cdr body))))) + (when (list.any remaining-defines? exprs) + (error "out of order define")) + (ir.make-lambda (list new-var) (rewrite-body expanded-body)))) + + + (define (builtin-call-builtin expr env time) + (unless (and (list? expr) + (>= (length expr) 2) + (tsvar? (cadr expr))) + (error "invalid form in builtin-call-builtin")) + (let ((builtin-name (tsvar-var (cadr expr))) + (args (cddr expr))) + (ir.make-call-builtin (string->symbol (ir.libvar-var builtin-name)) + (map + (lambda (arg) + (expand arg env (+ 1 time))) + args)))) + + + (define *builtins-environment* + (list.foldl + (lambda (acc x) + (map.insert acc (make-tsvar (ir.make-libvar "(csc builtins)" (car x)) 0) (cdr x))) + *empty-tsvar-map* + (list (cons "define" builtin-define) + (cons "lambda" builtin-lambda) + (cons "call-builtin" builtin-call-builtin)))) + ; Damn I can't believe we didn't actually implement syntax-rules. + + + (define (list-builtins) + (map + (lambda (x) + (string->symbol (ir.libvar-var (tsvar-var (car x))))) + (map.map->list *builtins-environment*))) + + + (define (expand-program prog) + (unstamp + (rewrite-body + (expand-body + (map + (lambda (x) + (timestamp x (stamper 0))) + prog) + *builtins-environment* + 1)))))) -- cgit v1.3.1