aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gensym.csc25
-rw-r--r--ir1.csc213
-rw-r--r--macros.csc184
3 files changed, 422 insertions, 0 deletions
diff --git a/gensym.csc b/gensym.csc
new file mode 100644
index 0000000..a7c30c5
--- /dev/null
+++ b/gensym.csc
@@ -0,0 +1,25 @@
+(define-library (csc gensym)
+ (export
+ gensym
+ gensym=?)
+ (import (scheme base))
+ (begin
+
+
+ (define-record-type <gensym>
+ (make-gensym id)
+ gensym?
+ (id gensym-id))
+
+
+ (define (gensym=? s1 s2)
+ (= (gensym-id s1) (gensym-id s2)))
+
+
+ (define *next-id* 0)
+
+
+ (define (gensym)
+ (let ((sym (make-gensym *next-id*)))
+ (set! *next-id* (+ 1 *next-id*))
+ sym))))
diff --git a/ir1.csc b/ir1.csc
new file mode 100644
index 0000000..53dacc1
--- /dev/null
+++ b/ir1.csc
@@ -0,0 +1,213 @@
+(define-library (csc ir1)
+ (export
+ call-arguments
+ call-procedure
+ call?
+ constant-expression
+ constant?
+ if-alternate
+ if-consequent
+ if-test
+ if?
+ lambda-body
+ lambda-case-alternate
+ lambda-case-arguments
+ lambda-case-body
+ lambda-case-gensyms
+ lambda-case-rest
+ lambda-case?
+ lambda?
+ letrec-expression
+ letrec-gensyms
+ letrec-in-order?
+ letrec-names
+ letrec-values
+ letrec?
+ lexical-ref-gensym
+ lexical-ref-name
+ lexical-ref?
+ lexical-set-expression
+ lexical-set-gensym
+ lexical-set-name
+ lexical-set?
+ library-ref-library
+ library-ref-name
+ library-ref-public?
+ library-ref?
+ library-set-expression
+ library-set-library
+ library-set-name
+ library-set-public?
+ library-set?
+ make-call
+ make-constant
+ make-if
+ make-lambda
+ make-lambda-case
+ make-letrec
+ make-lexical-ref
+ make-lexical-set
+ make-library-ref
+ make-library-set
+ make-sequence
+ make-toplevel-define
+ make-void
+ sequence-head
+ sequence-tail
+ sequence?
+ toplevel-define-expression
+ toplevel-define-name
+ toplevel-define?
+ void?)
+ (import (scheme base))
+ (begin
+ ; This library defines the intermediate representation IR1. An expression
+ ; in IR1 has one of the following forms (plagiarized from Guile's
+ ; Tree-IL).
+
+
+ ; <void>
+ ; An empty expression. In practice, equivalent to Scheme's (if #f #f).
+ (define-record-type <void>
+ (make-void)
+ void?)
+
+
+ ; <constant> expression
+ ; Constant is used to include literal constants in scheme code.
+ (define-record-type <constant>
+ (make-constant expression)
+ constant?
+ (expression constant-expression))
+
+
+ ; <lexical-ref> name gensym
+ ; A reference to a lexically-bound variable. The name is the original name
+ ; of the variable in the source program. gensym is a unique identifier for
+ ; this variable.
+ (define-record-type <lexical-ref>
+ (make-lexical-ref name gensym)
+ lexical-ref?
+ (name lexical-ref-name)
+ (gensym lexical-ref-gensym))
+
+
+ ; <lexical-set> name gensym expression
+ ; Sets a lexically-bound variable.
+ (define-record-type <lexical-set>
+ (make-lexical-set name gensym expression)
+ lexical-set?
+ (name lexical-set-name)
+ (gensym lexical-set-gensym)
+ (expression lexical-set-expression))
+
+
+ ; <library-ref> library name public?
+ ; A reference to a variable in a specific library. library should be the name
+ ; of the library, e.g. (scheme base).
+ ;
+ ; If public? is true, name will be looked up in library's public interface,
+ ; otherwise it will be looked up among the library's private bindings.
+ (define-record-type <library-ref>
+ (make-library-ref library name public?)
+ library-ref?
+ (library library-ref-library)
+ (name library-ref-name)
+ (public? library-ref-public?))
+
+
+ ; <library-set> library name public? expression
+ ; Sets a variable in a specific library.
+ (define-record-type <library-set>
+ (make-library-set library name public? expression)
+ library-set?
+ (library library-set-library)
+ (name library-set-name)
+ (public? library-set-public?)
+ (expression library-set-expression))
+
+
+ ; <toplevel-define> name expression
+ ; Defines a new variable in the current library.
+ (define-record-type <toplevel-define>
+ (make-toplevel-define name expression)
+ toplevel-define?
+ (name toplevel-define-name)
+ (expression toplevel-define-expression))
+
+
+ ; <if> test consequent alternate
+ ; A conditional.
+ (define-record-type <if>
+ (make-if test consequent alternate)
+ if?
+ (test if-test)
+ (consequent if-consequent)
+ (alternate if-alternate))
+
+
+ ; <call> procedure arguments
+ ; A procedure call. The procedure and arguments are evaluated in an
+ ; unspecified order, and the resulting procedure is passed the
+ ; resulting arguments.
+ (define-record-type <call>
+ (make-call procedure arguments)
+ call?
+ (procedure call-procedure)
+ (arguments call-arguments))
+
+
+ ; <sequence> head tail
+ ; Evaluate head, ignoring any result. Then tail is evaluated.
+ (define-record-type <sequence>
+ (make-sequence head tail)
+ sequence?
+ (head sequence-head)
+ (tail sequence-tail))
+
+
+ ; <lambda> body
+ ; A closure. body is an expression of type <lambda-case>.
+ (define-record-type <lambda>
+ (make-lambda body)
+ lambda?
+ (body lambda-body))
+
+
+ ; <lambda-case> arguments rest gensyms body alternate
+ ; One clause of a case-lambda. A lambda expression in Scheme is treated as
+ ; a case-lambda with one clause.
+ ;
+ ; arguments is a list of the procedures arguments, as symbols. rest is the
+ ; name of the rest argument, or #f. gensyms is a list of gensyms
+ ; corresponding to all arguments: first all of the normal arguments, then
+ ; the rest argument if any.
+ ;
+ ; body is the name of the clause. If the procedure is called with an
+ ; appropriate number of arguments, body is evaluated in tail position.
+ ; Otherwise if there is an alternate, it should be a <lambda-case>
+ ; expression, representing the next clause to try. If there is no
+ ; alternate, an error is signaled.
+ (define-record-type <lambda-case>
+ (make-lambda-case arguments rest gensyms body alternate)
+ lambda-case?
+ (arguments lambda-case-arguments)
+ (rest lambda-case-rest)
+ (gensyms lambda-case-gensyms)
+ (body lambda-case-body)
+ (alternate lambda-case-alternate))
+
+
+ ; <letrec> in-order? names gensyms values expression
+ ; Lexical binding, like Scheme's letrec, or letrec* if in-order? is true.
+ ; names are the original binding names, gensyms are gensyms corresponding
+ ; to the names, and values are IR1 expressions for the values. expression
+ ; is a single IR1 expression.
+ (define-record-type <letrec>
+ (make-letrec in-order? names gensyms values expression)
+ letrec?
+ (in-order? letrec-in-order?)
+ (names letrec-names)
+ (gensyms letrec-gensyms)
+ (values letrec-values)
+ (expression letrec-expression))))
diff --git a/macros.csc b/macros.csc
new file mode 100644
index 0000000..a41a4a7
--- /dev/null
+++ b/macros.csc
@@ -0,0 +1,184 @@
+(define-library (csc macros)
+ (export
+ expand
+ test-environment)
+ (import (scheme base)
+ (only (csc gensym) gensym)
+ (only (csc hash-map)
+ alist->map
+ hash-bytevector
+ insert
+ key-not-found-error?
+ lookup)
+ (only (csc ir1)
+ make-call
+ make-constant
+ make-lambda
+ make-lambda-case)
+ (only (csc list) revappend)
+ (only (csc match) match))
+ (begin
+
+
+ (define-record-type <macro-transformer>
+ (make-macro-transformer transformer)
+ macro-transformer?
+ (transformer transformer-function))
+
+
+ (define-record-type <macro-syntax-error>
+ (make-macro-syntax-error message irritants)
+ macro-syntax-error?
+ (message syntax-error-object-message)
+ (irritants syntax-error-object-irritants))
+
+
+ (define (raise-syntax-error message . irritants)
+ (raise (make-macro-syntax-error message irritants)))
+
+
+ ; symbols is a map with symbols as keys, and the values can be one of:
+ ; - <lexical-ref>,
+ ; - <module-ref>,
+ ; - or <macro-transformer>.
+ ; The first two correspond to variables bound lexically or from a module,
+ ; and the third represents a macro transformer bound in the
+ ; current context.
+ ;
+ ; library is the current library name being compiled. A nil library
+ ; corresponds to top level expressions.
+ (define-record-type <environment>
+ (make-environment symbols library)
+ environment?
+ (symbols environment-symbols)
+ (library environment-library))
+
+
+ (define (with-binding environment symbol binding)
+ (make-environment (insert (environment-symbols environment) symbol binding) (environment-library environment)))
+
+
+ (define (expand-procedure-call procedure arguments environment)
+ (let*-values (((expanded-procedure environment) (expand procedure environment))
+ ((expanded-arguments environment)
+ (let loop ((arguments arguments)
+ (environment environment)
+ (expanded-arguments '()))
+ (match arguments
+ ('() (values (reverse expanded-arguments) environment))
+ ((argument . rest)
+ (let-values (((expanded-argument environment) (expand argument environment)))
+ (loop
+ rest
+ environment
+ (cons expanded-argument expanded-arguments))))
+ (_ (raise-syntax-error "arguments to a procedure call must be a list" procedure arguments))))))
+ (make-call expanded-procedure expanded-arguments)))
+
+
+ ; expand can be thought of as a compiler from Scheme to IR1. Macros
+ ; included in the environment can be used to extend the syntax. Returns an
+ ; IR1 expression and an environment which has been modified with any new
+ ; bindings introduced by the expression.
+ (define (expand expression environment)
+ (cond
+ ((null? expression) (raise-syntax-error "nil by itself is an error (did you mean to use quote?)" expression))
+ ((and (pair? expression)
+ (symbol? (car expression)))
+ (let* ((macro-name (car expression))
+ (macro-body
+ (guard (e ((key-not-found-error? e) (raise-syntax-error "undefined symbol" macro-name)))
+ (lookup (environment-symbols environment) macro-name))))
+ (if (macro-transformer? macro-body)
+ ((transformer-function macro-body) expression environment)
+ (expand-procedure-call macro-name (cdr expression) environment))))
+ ((pair? expression)
+ (let ((procedure (car expression))
+ (arguments (cdr expression)))
+ (expand-procedure-call procedure arguments environment)))
+ ((symbol? expression)
+ (let ((binding
+ (guard (e ((key-not-found-error? e) (raise-syntax-error "undefined symbol" expression)))
+ (lookup (environment-symbols environment) expression))))
+ (if (macro-transformer? binding)
+ (raise-syntax-error "macro is not allowed in this context" expression)
+ (values binding environment))))
+ ((or (boolean? expression)
+ (bytevector? expression)
+ (char? expression)
+ (number? expression)
+ (string? expression)
+ (vector? expression))
+ (values (make-constant expression) environment))
+ (else (raise-syntax-error "unexpected expression type" expression))))
+
+
+ (define builtin-quote
+ (make-macro-transformer
+ (lambda (expression environment)
+ (match expression
+ ((_ datum) (values (make-constant datum) environment))
+ (_ (raise-syntax-error "invalid form for quote" expression))))))
+
+
+ (define builtin-lambda
+ (make-macro-transformer
+ (lambda (expression environment)
+ (match expression
+ ((_ formals body)
+ (let loop ((formals formals)
+ (environment environment)
+ (argument-names '())
+ (gensyms '()))
+ (match formals
+ ('()
+ (make-lambda
+ (make-lambda-case
+ (reverse argument-names)
+ #f
+ (reverse gensyms)
+ (expand body environment)
+ #f)))
+ ((variable . variables) (when (symbol? variable))
+ (let ((sym (gensym)))
+ (loop
+ variables
+ (with-binding environment variable sym)
+ (cons variable argument-names)
+ (cons sym gensyms))))
+ (variable (when (symbol? variable))
+ (let ((sym (gensym)))
+ (make-lambda
+ (make-lambda-case
+ (reverse argument-names)
+ variable
+ (revappend gensyms (list sym))
+ (expand body (with-binding environment variable sym))
+ #f))))
+ (_ (raise-syntax-error "invalid form for lambda arguments" expression)))))
+ (_ (raise-syntax-error "invalid form for lambda" expression))))))
+
+
+ #;(define builtin-syntax-rules
+ (make-macro-transformer
+ (lambda (expression environment)
+ (match expression))))
+
+
+ (define (hash-symbol s)
+ (hash-bytevector (string->utf8 (symbol->string s))))
+
+
+ (define (symbol<? s1 s2)
+ (string<? (symbol->string s1) (symbol->string s2)))
+
+
+ (define test-environment
+ (make-environment
+ (alist->map
+ hash-symbol
+ symbol<?
+ (list
+ (cons 'quote builtin-quote)
+ (cons 'lambda builtin-lambda)))
+ '()))))