;;; Guile High Intermediate Language ;; Copyright (C) 2001 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Code: (define-module (language ghil) #:use-module (system base syntax) #:use-module (system base pmatch) #:use-module (ice-9 regex) #:export (ghil-env ghil-loc make-ghil-void ghil-void? ghil-void-env ghil-void-loc make-ghil-quote ghil-quote? ghil-quote-env ghil-quote-loc ghil-quote-obj make-ghil-quasiquote ghil-quasiquote? ghil-quasiquote-env ghil-quasiquote-loc ghil-quasiquote-exp make-ghil-unquote ghil-unquote? ghil-unquote-env ghil-unquote-loc ghil-unquote-exp make-ghil-unquote-splicing ghil-unquote-splicing? ghil-unquote-splicing-env ghil-unquote-splicing-loc ghil-unquote-splicing-exp make-ghil-ref ghil-ref? ghil-ref-env ghil-ref-loc ghil-ref-var make-ghil-set ghil-set? ghil-set-env ghil-set-loc ghil-set-var ghil-set-val make-ghil-define ghil-define? ghil-define-env ghil-define-loc ghil-define-var ghil-define-val make-ghil-if ghil-if? ghil-if-env ghil-if-loc ghil-if-test ghil-if-then ghil-if-else make-ghil-and ghil-and? ghil-and-env ghil-and-loc ghil-and-exps make-ghil-or ghil-or? ghil-or-env ghil-or-loc ghil-or-exps make-ghil-begin ghil-begin? ghil-begin-env ghil-begin-loc ghil-begin-exps make-ghil-bind ghil-bind? ghil-bind-env ghil-bind-loc ghil-bind-vars ghil-bind-vals ghil-bind-body make-ghil-mv-bind ghil-mv-bind? ghil-mv-bind-env ghil-mv-bind-loc ghil-mv-bind-producer ghil-mv-bind-vars ghil-mv-bind-rest ghil-mv-bind-body make-ghil-lambda ghil-lambda? ghil-lambda-env ghil-lambda-loc ghil-lambda-vars ghil-lambda-rest ghil-lambda-meta ghil-lambda-body make-ghil-inline ghil-inline? ghil-inline-env ghil-inline-loc ghil-inline-inline ghil-inline-args make-ghil-call ghil-call? ghil-call-env ghil-call-loc ghil-call-proc ghil-call-args make-ghil-mv-call ghil-mv-call? ghil-mv-call-env ghil-mv-call-loc ghil-mv-call-producer ghil-mv-call-consumer make-ghil-values ghil-values? ghil-values-env ghil-values-loc ghil-values-values make-ghil-values* ghil-values*? ghil-values*-env ghil-values*-loc ghil-values*-values make-ghil-var ghil-var? ghil-var-env ghil-var-name ghil-var-kind ghil-var-index make-ghil-toplevel-env ghil-toplevel-env? ghil-toplevel-env-table make-ghil-env ghil-env? ghil-env-parent ghil-env-table ghil-env-variables make-ghil-reified-env ghil-reified-env? ghil-reified-env-env ghil-reified-env-loc ghil-env-add! ghil-env-reify ghil-env-dereify ghil-var-is-bound? ghil-var-for-ref! ghil-var-for-set! ghil-var-define! ghil-var-at-module! call-with-ghil-environment call-with-ghil-bindings parse-ghil unparse-ghil)) ;;; ;;; Parse tree ;;; (define (print-ghil x port) (format port "#" (unparse-ghil x))) (define-type ( #:printer print-ghil #:common-slots (env loc)) ;; Objects () ( obj) ( exp) ( exp) ( exp) ;; Variables ( var) ( var val) ( var val) ;; Controls ( test then else) ( exps) ( exps) ( exps) ( vars vals body) ( producer vars rest body) ( vars rest meta body) ( proc args) ( producer consumer) ( inline args) ( values) ( values) ()) ;;; ;;; Variables ;;; (define-record env name kind (index #f)) ;;; ;;; Modules ;;; ;;; ;;; Environments ;;; (define-record parent (table '()) (variables '())) (define-record (table '())) (define (ghil-env-ref env sym) (assq-ref (ghil-env-table env) sym)) (define-macro (push! item loc) `(set! ,loc (cons ,item ,loc))) (define-macro (apush! k v loc) `(set! ,loc (acons ,k ,v ,loc))) (define-macro (apopq! k loc) `(set! ,loc (assq-remove! ,loc ,k))) (define (ghil-env-add! env var) (apush! (ghil-var-name var) var (ghil-env-table env)) (push! var (ghil-env-variables env))) (define (ghil-env-remove! env var) (apopq! (ghil-var-name var) (ghil-env-table env))) (define (force-heap-allocation! var) (set! (ghil-var-kind var) 'external)) ;;; ;;; Public interface ;;; ;; The following four functions used to be one, in ghil-lookup. Now they ;; are four, to reflect the different intents. A bit of duplication, but ;; that's OK. The common current is to find out where a variable will be ;; stored at runtime. ;; ;; These functions first search the lexical environments. If the ;; variable is not in the innermost environment, make sure the variable ;; is marked as being "external" so that it goes on the heap. If the ;; variable is being modified (via a set!), also make sure it's on the ;; heap, so that other continuations see the changes to the var. ;; ;; If the variable is not found lexically, it is a toplevel variable, ;; which will be looked up at runtime with respect to the module that ;; was current when the lambda was bound, at runtime. The variable will ;; be resolved when it is first used. (define (ghil-var-is-bound? env sym) (let loop ((e env)) (record-case e (( table) (let ((key (cons (module-name (current-module)) sym))) (assoc-ref table key))) (( parent table variables) (and (not (assq-ref table sym)) (loop parent)))))) (define (ghil-var-for-ref! env sym) (let loop ((e env)) (record-case e (( table) (let ((key (cons (module-name (current-module)) sym))) (or (assoc-ref table key) (let ((var (make-ghil-var (car key) (cdr key) 'toplevel))) (apush! key var (ghil-toplevel-env-table e)) var)))) (( parent table variables) (cond ((assq-ref table sym) => (lambda (var) (or (eq? e env) (force-heap-allocation! var)) var)) (else (loop parent))))))) (define (ghil-var-for-set! env sym) (let loop ((e env)) (record-case e (( table) (let ((key (cons (module-name (current-module)) sym))) (or (assoc-ref table key) (let ((var (make-ghil-var (car key) (cdr key) 'toplevel))) (apush! key var (ghil-toplevel-env-table e)) var)))) (( parent table variables) (cond ((assq-ref table sym) => (lambda (var) (force-heap-allocation! var) var)) (else (loop parent))))))) (define (ghil-var-at-module! env modname sym interface?) (let loop ((e env)) (record-case e (( table) (let ((key (list modname sym interface?))) (or (assoc-ref table key) (let ((var (make-ghil-var modname sym (if interface? 'public 'private)))) (apush! key var (ghil-toplevel-env-table e)) var)))) (( parent table variables) (loop parent))))) (define (ghil-var-define! toplevel sym) (let ((key (cons (module-name (current-module)) sym))) (or (assoc-ref (ghil-toplevel-env-table toplevel) key) (let ((var (make-ghil-var (car key) (cdr key) 'toplevel))) (apush! key var (ghil-toplevel-env-table toplevel)) var)))) (define (call-with-ghil-environment e syms func) (let* ((e (make-ghil-env e)) (vars (map (lambda (s) (let ((v (make-ghil-var e s 'argument))) (ghil-env-add! e v) v)) syms))) (func e vars))) (define (call-with-ghil-bindings e syms func) (let* ((vars (map (lambda (s) (let ((v (make-ghil-var e s 'local))) (ghil-env-add! e v) v)) syms)) (ret (func vars))) (for-each (lambda (v) (ghil-env-remove! e v)) vars) ret)) (define (ghil-env-reify env) (let loop ((e env) (out '())) (record-case e (( table) (map (lambda (v) (cons (ghil-var-name v) (or (ghil-var-index v) (error "reify called before indices finalized")))) out)) (( parent table variables) (loop parent (append out (filter (lambda (v) (eq? (ghil-var-kind v) 'external)) variables))))))) (define (ghil-env-dereify name-index-alist) (let* ((e (make-ghil-env (make-ghil-toplevel-env))) (vars (map (lambda (pair) (make-ghil-var e (car pair) 'external (cdr pair))) name-index-alist))) (set! (ghil-env-table e) (map (lambda (v) (cons (ghil-var-name v) v)) vars)) (set! (ghil-env-variables e) vars) e)) ;;; ;;; Parser ;;; (define (location x) (and (pair? x) (let ((props (source-properties x))) (and (not (null? props)) (vector (assq-ref props 'line) (assq-ref props 'column) (assq-ref props 'filename)))))) (define (parse-quasiquote e x level) (cond ((not (pair? x)) x) ((memq (car x) '(unquote unquote-splicing)) (let ((l (location x))) (pmatch (cdr x) ((,obj) (cond ((zero? level) (if (eq? (car x) 'unquote) (make-ghil-unquote e l (parse-ghil e obj)) (make-ghil-unquote-splicing e l (parse-ghil e obj)))) (else (list (car x) (parse-quasiquote e obj (1- level)))))) (else (syntax-error l (format #f "bad ~A" (car x)) x))))) ((eq? (car x) 'quasiquote) (let ((l (location x))) (pmatch (cdr x) ((,obj) (list 'quasiquote (parse-quasiquote e obj (1+ level)))) (else (syntax-error l (format #f "bad ~A" (car x)) x))))) (else (cons (parse-quasiquote e (car x) level) (parse-quasiquote e (cdr x) level))))) (define (parse-ghil env exp) (let ((loc (location exp)) (retrans (lambda (x) (parse-ghil env x)))) (pmatch exp ((ref ,sym) (guard (symbol? sym)) (make-ghil-ref env #f (ghil-var-for-ref! env sym))) (('quote ,exp) (make-ghil-quote env loc exp)) ((void) (make-ghil-void env loc)) ((lambda ,syms ,rest ,meta . ,body) (call-with-ghil-environment env syms (lambda (env vars) (make-ghil-lambda env loc vars rest meta (parse-ghil env `(begin ,@body)))))) ((begin . ,body) (make-ghil-begin env loc (map retrans body))) ((bind ,syms ,exprs . ,body) (let ((vals (map retrans exprs))) (call-with-ghil-bindings env syms (lambda (vars) (make-ghil-bind env loc vars vals (retrans `(begin ,@body))))))) ((bindrec ,syms ,exprs . ,body) (call-with-ghil-bindings env syms (lambda (vars) (let ((vals (map (lambda (exp) (parse-ghil env exp)) exprs))) (make-ghil-bind env loc vars vals (retrans `(begin ,@body))))))) ((set ,sym ,val) (make-ghil-set env loc (ghil-var-for-set! env sym) (retrans val))) ((define ,sym ,val) (make-ghil-define env loc (ghil-var-define! env sym) (retrans val))) ((if ,test ,then ,else) (make-ghil-if env loc (retrans test) (retrans then) (retrans else))) ((and . ,exps) (make-ghil-and env loc (map retrans exps))) ((or . ,exps) (make-ghil-or env loc (map retrans exps))) ((mv-bind ,syms ,rest ,producer . ,body) (call-with-ghil-bindings env syms (lambda (vars) (make-ghil-mv-bind env loc (retrans producer) vars rest (map retrans body))))) ((call ,proc . ,args) (make-ghil-call env loc (retrans proc) (map retrans args))) ((mv-call ,producer ,consumer) (make-ghil-mv-call env loc (retrans producer) (retrans consumer))) ((inline ,op . ,args) (make-ghil-inline env loc op (map retrans args))) ((values . ,values) (make-ghil-values env loc (map retrans values))) ((values* . ,values) (make-ghil-values* env loc (map retrans values))) ((compile-time-environment) (make-ghil-reified-env env loc)) ((quasiquote ,exp) (make-ghil-quasiquote env loc (parse-quasiquote env exp 0))) (else (error "unrecognized GHIL" exp))))) (define (unparse-ghil ghil) (record-case ghil (( env loc) '(void)) (( env loc obj) `(,'quote ,obj)) (( env loc exp) `(,'quasiquote ,(let lp ((x exp)) (cond ((struct? x) (unparse-ghil x)) ((pair? x) (cons (lp (car x)) (lp (cdr x)))) (else x))))) (( env loc exp) `(,'unquote ,(unparse-ghil exp))) (( env loc exp) `(,'unquote-splicing ,(unparse-ghil exp))) ;; Variables (( env loc var) `(ref ,(ghil-var-name var))) (( env loc var val) `(set ,(ghil-var-name var) ,(unparse-ghil val))) (( env loc var val) `(define ,(ghil-var-name var) ,(unparse-ghil val))) ;; Controls (( env loc test then else) `(if ,(unparse-ghil test) ,(unparse-ghil then) ,(unparse-ghil else))) (( env loc exps) `(and ,@(map unparse-ghil exps))) (( env loc exps) `(or ,@(map unparse-ghil exps))) (( env loc exps) `(begin ,@(map unparse-ghil exps))) (( env loc vars vals body) `(bind ,(map ghil-var-name vars) ,(map unparse-ghil vals) ,(unparse-ghil body))) (( env loc producer vars rest body) `(mv-bind ,(map ghil-var-name vars) ,rest ,(unparse-ghil producer) ,(unparse-ghil body))) (( env loc vars rest meta body) `(lambda ,(map ghil-var-name vars) ,rest ,meta ,(unparse-ghil body))) (( env loc proc args) `(call ,(unparse-ghil proc) ,@(map unparse-ghil args))) (( env loc producer consumer) `(mv-call ,(unparse-ghil producer) ,(unparse-ghil consumer))) (( env loc inline args) `(inline ,inline ,@(map unparse-ghil args))) (( env loc values) `(values ,@(map unparse-ghil values))) (( env loc values) `(values* ,@(map unparse-ghil values))) (( env loc) `(compile-time-environment))))