1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-28 16:00:22 +02:00
guile/module/language/scheme/translate.scm
Andy Wingo 849cefacf1 unify variant types and records; also make-foo instead of <foo>
* module/system/base/syntax.scm (define-record): Rework to separate the
  type and its constructor. Now (define-record (<foo> bar)) will create
  `make-foo' as the constructor, not `<foo>'. Also the constructor now
  takes either keyword or positional arguments, so that it can be used as
  the implementation of variant types as well.
  (|): Map directly to define-record instead of rolling our own thing.

* module/language/scheme/translate.scm:
* module/system/base/language.scm:
* module/system/il/compile.scm:
* module/system/il/ghil.scm:
* module/system/il/glil.scm:
* module/system/repl/common.scm:
* module/system/vm/assemble.scm:
* module/system/vm/debug.scm: Change instances of record creation to use
  the make-foo procedures instead of <foo>. Adjust module exports as
  necessary.
2008-05-03 18:32:46 +02:00

396 lines
12 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; Guile Scheme specification
;; 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 scheme translate)
:use-module (system base language)
:use-module (system il ghil)
:use-module (ice-9 match)
:use-module (ice-9 receive)
:use-module (srfi srfi-39)
:use-module ((system base compile) :select (syntax-error))
:export (translate))
;; Module in which compile-time code (macros) is evaluated.
(define &compile-time-module (make-parameter #f))
(define (eval-at-compile-time exp)
"Evaluate @var{exp} in the current compile-time module."
(catch #t
(lambda ()
(save-module-excursion
(lambda ()
(eval exp (&compile-time-module)))))
(lambda (key . args)
(syntax-error #f
(format #f "~a: compile-time evaluation failed" exp)
(cons key args)))))
(define (translate x e)
(parameterize ((&compile-time-module (make-module)))
;; Import only core bindings in the macro module.
(module-use! (&compile-time-module) the-root-module)
(call-with-ghil-environment (make-ghil-mod e) '()
(lambda (env vars)
(make-ghil-lambda env #f vars #f (trans env #f x))))))
;;;
;;; Macro tricks
;;;
(define (expand-macro e)
;; Similar to `macroexpand' in `boot-9.scm' except that it does not expand
;; `define-macro' and `defmacro'.
(cond
((pair? e)
(let* ((head (car e))
(val (and (symbol? head)
(false-if-exception
(module-ref (&compile-time-module) head)))))
(case head
((defmacro define-macro)
;; Normally, these are expanded as `defmacro:transformer' but we
;; don't want it to happen since they are handled by `trans-pair'.
e)
((use-syntax)
;; `use-syntax' is used to express a compile-time dependency
;; (because we use a macro from that module, or because one of our
;; macros uses bindings from that module). Thus, we arrange to get
;; the current compile-time module to use it.
(let* ((module-name (cadr e))
(module (false-if-exception (resolve-module module-name))))
(if (module? module)
(let ((public-if (module-public-interface module)))
(module-use! (&compile-time-module) public-if))
(syntax-error #f "invalid `use-syntax' form" e)))
'(void))
((begin let let* letrec lambda quote quasiquote if and or
set! cond case eval-case define do)
;; All these built-in macros should not be expanded.
e)
(else
;; Look for a macro.
(let ((ref (false-if-exception
(module-ref (&compile-time-module) head))))
(if (macro? ref)
(expand-macro
(save-module-excursion
(lambda ()
(let ((transformer (macro-transformer ref))
(syntax-error syntax-error))
(set-current-module (&compile-time-module))
(catch #t
(lambda ()
(transformer (copy-tree e) (current-module)))
(lambda (key . args)
(syntax-error #f
(format #f "~a: macro transformer failed"
head)
(cons key args))))))))
e))))))
(#t e)))
;;;
;;; Translator
;;;
(define %scheme-primitives
'(not null? eq? eqv? equal? pair? list? cons car cdr set-car! set-cdr!))
(define %forbidden-primitives
;; Guile's `procedure->macro' family is evil because it crosses the
;; compilation boundary. One solution might be to evaluate calls to
;; `procedure->memoizing-macro' at compilation time, but it may be more
;; compicated than that.
'(procedure->syntax procedure->macro procedure->memoizing-macro))
(define (trans e l x)
(cond ((pair? x)
(let ((y (expand-macro x)))
(if (eq? x y)
(trans-pair e (or (location x) l) (car x) (cdr x))
(trans e l y))))
((symbol? x)
(let ((y (symbol-expand x)))
(if (symbol? y)
(make-ghil-ref e l (ghil-lookup e y))
(trans e l y))))
(else (make-ghil-quote e l x))))
(define (symbol-expand x)
(let loop ((s (symbol->string x)))
(let ((i (string-rindex s #\.)))
(if i
(let ((sym (string->symbol (substring s (1+ i)))))
`(slot ,(loop (substring s 0 i)) (quote ,sym)))
(string->symbol s)))))
(define (trans-pair e l head tail)
(define (trans:x x) (trans e l x))
(define (trans:pair x) (trans-pair e l (car x) (cdr x)))
(define (trans:body body) (trans-body e l body))
(define (make:void) (make-ghil-void e l))
(define (bad-syntax)
(syntax-error l (format #f "bad ~A" head) (cons head tail)))
(case head
;; (void)
((void)
(match tail
(() (make:void))
(else (bad-syntax))))
;; (quote OBJ)
((quote)
(match tail
((obj) (make-ghil-quote e l obj))
(else (bad-syntax))))
;; (quasiquote OBJ)
((quasiquote)
(match tail
((obj) (make-ghil-quasiquote e l (trans-quasiquote e l obj)))
(else (bad-syntax))))
((define define-private)
(match tail
;; (define NAME VAL)
(((? symbol? name) val)
(make-ghil-define e l (ghil-lookup e name) (trans:x val)))
;; (define (NAME FORMALS...) BODY...)
((((? symbol? name) . formals) . body)
;; -> (define NAME (lambda FORMALS BODY...))
(let ((val (trans:x `(lambda ,formals ,@body))))
(make-ghil-define e l (ghil-lookup e name) val)))
(else (bad-syntax))))
;; simple macros
((defmacro define-macro)
;; Evaluate the macro definition in the current compile-time module.
(eval-at-compile-time (cons head tail))
;; FIXME: We need to evaluate them in the runtime module as well.
(make:void))
((set!)
(match tail
;; (set! NAME VAL)
(((? symbol? name) val)
(make-ghil-set e l (ghil-lookup e name) (trans:x val)))
;; (set! (NAME ARGS...) VAL)
((((? symbol? name) . args) val)
;; -> ((setter NAME) ARGS... VAL)
(trans:pair `((setter ,name) . (,@args ,val))))
(else (bad-syntax))))
;; (if TEST THEN [ELSE])
((if)
(match tail
((test then)
(make-ghil-if e l (trans:x test) (trans:x then) (make:void)))
((test then else)
(make-ghil-if e l (trans:x test) (trans:x then) (trans:x else)))
(else (bad-syntax))))
;; (and EXPS...)
((and)
(make-ghil-and e l (map trans:x tail)))
;; (or EXPS...)
((or)
(make-ghil-or e l (map trans:x tail)))
;; (begin EXPS...)
((begin)
(make-ghil-begin e l (map trans:x tail)))
((let)
(match tail
;; (let NAME ((SYM VAL) ...) BODY...)
(((? symbol? name) (((? symbol? sym) val) ...) body ...)
;; -> (letrec ((NAME (lambda (SYM...) BODY...))) (NAME VAL...))
(trans:pair `(letrec ((,name (lambda ,sym ,@body))) (,name ,@val))))
;; (let () BODY...)
((() body ...)
;; NOTE: This differs from `begin'
(make-ghil-begin e l (list (trans:body body))))
;; (let ((SYM VAL) ...) BODY...)
(((((? symbol? sym) val) ...) body ...)
(let ((vals (map trans:x val)))
(call-with-ghil-bindings e sym
(lambda (vars)
(make-ghil-bind e l vars vals (trans:body body))))))
(else (bad-syntax))))
;; (let* ((SYM VAL) ...) BODY...)
((let*)
(match tail
(((def ...) body ...)
(if (null? def)
(trans:pair `(let () ,@body))
(trans:pair `(let (,(car def)) (let* ,(cdr def) ,@body)))))
(else (bad-syntax))))
;; (letrec ((SYM VAL) ...) BODY...)
((letrec)
(match tail
(((((? symbol? sym) val) ...) body ...)
(call-with-ghil-bindings e sym
(lambda (vars)
(let ((vals (map trans:x val)))
(make-ghil-bind e l vars vals (trans:body body))))))
(else (bad-syntax))))
;; (cond (CLAUSE BODY...) ...)
((cond)
(match tail
(() (make:void))
((('else . body)) (trans:body body))
(((test) . rest) (trans:pair `(or ,test (cond ,@rest))))
(((test '=> proc) . rest)
(trans:pair `(let ((_t ,test)) (if _t (,proc _t) (cond ,@rest)))))
(((test . body) . rest)
(trans:pair `(if ,test (begin ,@body) (cond ,@rest))))
(else (bad-syntax))))
;; (case EXP ((KEY...) BODY...) ...)
((case)
(match tail
((exp . clauses)
(trans:pair
`(let ((_t ,exp))
,(let loop ((ls clauses))
(cond ((null? ls) '(void))
((eq? (caar ls) 'else) `(begin ,@(cdar ls)))
(else `(if (memv _t ',(caar ls))
(begin ,@(cdar ls))
,(loop (cdr ls)))))))))
(else (bad-syntax))))
;; (do ((SYM VAL [UPDATE]) ...) (TEST RESULT...) BODY...)
((do)
(let ()
(define (next s x) (if (pair? x) (car x) s))
(match tail
((((sym init . update) ...) (test . result) body ...)
(trans:pair
`(letrec ((_l (lambda ,sym
(if ,test
(let () (void) ,@result)
(let () (void) ,@body
(_l ,@(map next sym update)))))))
(_l ,@init))))
(else (bad-syntax)))))
;; (lambda FORMALS BODY...)
((lambda)
(match tail
((formals body ...)
(receive (syms rest) (parse-formals formals)
(call-with-ghil-environment e syms
(lambda (env vars)
(make-ghil-lambda env l vars rest (trans-body env l body))))))
(else (bad-syntax))))
((eval-case)
(let loop ((x tail))
(match x
(() (make:void))
((('else . body)) (trans:pair `(begin ,@body)))
(((((? symbol? key) ...) body ...) rest ...)
(if (memq 'load-toplevel key)
(begin
(primitive-eval `(begin ,@(copy-tree body)))
(trans:pair `(begin ,@body)))
(loop rest)))
(else (bad-syntax)))))
(else
(if (memq head %scheme-primitives)
(make-ghil-inline e l head (map trans:x tail))
(if (memq head %forbidden-primitives)
(syntax-error l (format #f "`~a' is forbidden" head)
(cons head tail))
(make-ghil-call e l (trans:x head) (map trans:x tail)))))))
(define (trans-quasiquote e l x)
(cond ((not (pair? x)) x)
((memq (car x) '(unquote unquote-splicing))
(let ((l (location x)))
(match (cdr x)
((obj)
(if (eq? (car x) 'unquote)
(make-ghil-unquote e l (trans e l obj))
(make-ghil-unquote-splicing e l (trans e l obj))))
(else (syntax-error l (format #f "bad ~A" (car x)) x)))))
(else (cons (trans-quasiquote e l (car x))
(trans-quasiquote e l (cdr x))))))
(define (trans-body e l body)
(define (define->binding df)
(match (cdr df)
(((? symbol? name) val) (list name val))
((((? symbol? name) . formals) . body)
(list name `(lambda ,formals ,@body)))
(else (syntax-error (location df) "bad define" df))))
;; main
(let loop ((ls body) (ds '()))
(cond ((null? ls) (syntax-error l "bad body" body))
((and (pair? (car ls)) (eq? (caar ls) 'define))
(loop (cdr ls) (cons (car ls) ds)))
(else
(if (null? ds)
(trans-pair e l 'begin ls)
(trans-pair e l 'letrec (cons (map define->binding ds) ls)))))))
(define (parse-formals formals)
(cond
;; (lambda x ...)
((symbol? formals) (values (list formals) #t))
;; (lambda (x y z) ...)
((list? formals) (values formals #f))
;; (lambda (x y . z) ...)
((pair? formals)
(let loop ((l formals) (v '()))
(if (pair? l)
(loop (cdr l) (cons (car l) v))
(values (reverse! (cons l v)) #t))))
(else (syntax-error (location formals) "bad formals" formals))))
(define (location x)
(and (pair? x)
(let ((props (source-properties x)))
(and (not (null? props))
(cons (assq-ref props 'line) (assq-ref props 'column))))))