(exception:string-contains-nul): New exception pattern.
[bpt/guile.git] / lang / elisp / transform.scm
dissimilarity index 71%
index 0bb28ea..ee288a7 100644 (file)
-(define-module (lang elisp transform)
-  #:use-module (lang elisp internals trace)
-  #:use-module (lang elisp internals fset)
-  #:use-module (lang elisp internals evaluation)
-  #:use-module (ice-9 session)
-  #:export (transformer transform))
-
-;;; {S-expressions}
-;;;
-
-(define (syntax-error x)
-  (error "Syntax error in expression" x))
-
-;; Should be made mutating instead of constructing
-;;
-(define (transformer x)
-  (cond ((eq? x 'nil) %nil)
-       ((eq? x 't) #t)
-       ((null? x) '())
-       ((not (pair? x)) x)
-       ((and (pair? (car x))
-             (eq? (caar x) 'quasiquote))
-        (transformer (car x)))
-       ((symbol? (car x))
-        (case (car x)
-          ((@fop @bind define-module use-modules use-syntax) x)
-          ; Escape to Scheme syntax
-          ((scheme) (cons begin (cdr x)))
-          ; Should be handled in reader
-          ((quote function) `(,quote ,@(cars->nil (cdr x))))
-          ((quasiquote) (m-quasiquote x '()))
-          ;((nil-cond) (transform-1 x))
-          ;((let) (m-let x '()))
-          ;((let*) (m-let* x '()))
-          ;((if) (m-if x '()))
-          ;((and) (m-and x '()))
-          ;((or) (m-or x '()))
-          ;((while) (m-while x '()))
-          ;((while) (cons macro-while (cdr x)))
-          ;((prog1) (m-prog1 x '()))
-          ;((prog2) (m-prog2 x '()))
-          ;((progn) (cons 'begin (map transformer (cdr x))))
-          ;((cond) (m-cond x '()))
-          ;((lambda) (transform-lambda/interactive x '<elisp-lambda>))
-          ;((defun) (m-defun x '()))
-          ;((defmacro) (m-defmacro x '()))
-          ;((setq) (m-setq x '()))
-          ;((interactive) (fluid-set! interactive-spec x) #f)
-          ;((unwind-protect) (m-unwind-protect x '()))
-          (else (transform-application x))))
-       (else (syntax-error x))))
-
-(define (m-quasiquote exp env)
-  (cons 'quasiquote
-       (map transform-inside-qq (cdr exp))))
-
-(define (transform-inside-qq x)
-  (trc 'transform-inside-qq x)
-  (cond ((not (pair? x)) x)
-       ((symbol? (car x))
-        (case (car x)
-          ((unquote) (list 'unquote (transformer (cadr x))))
-          ((unquote-splicing) (list 'unquote-splicing (transformer (cadr x))))
-          (else (cons (car x) (map transform-inside-qq (cdr x))))))
-       (else
-        (cons (transform-inside-qq (car x)) (transform-inside-qq (cdr x))))))
-
-(define (transform-application x)
-  (cons-source x
-              '@fop
-              `(,(car x) (,transformer-macro ,@(cdr x)))))
-
-(define transformer-macro
-  (procedure->memoizing-macro
-   (let ((cdr cdr))
-     (lambda (exp env)
-       (cons 'list (map transformer (cdr exp)))))))
-
-(define (cars->nil ls)
-  (cond ((not (pair? ls)) ls)
-       ((null? (car ls)) (cons '() (cars->nil (cdr ls))))
-       (else (cons (cars->nil (car ls))
-                   (cars->nil (cdr ls))))))
-
-(define transform transformer)
+(define-module (lang elisp transform)
+  #:use-module (lang elisp internals trace)
+  #:use-module (lang elisp internals fset)
+  #:use-module (lang elisp internals evaluation)
+  #:use-module (ice-9 session)
+  #:export (transformer transform))
+
+;;; A note on the difference between `(transform-* (cdr x))' and `(map
+;;; transform-* (cdr x))'.
+;;;
+;;; In most cases, none, as most of the transform-* functions are
+;;; recursive.
+;;;
+;;; However, if (cdr x) is not a proper list, the `map' version will
+;;; signal an error immediately, whereas the non-`map' version will
+;;; produce a similarly improper list as its transformed output.  In
+;;; some cases, improper lists are allowed, so at least these cases
+;;; require non-`map'.
+;;;
+;;; Therefore we use the non-`map' approach in most cases below, but
+;;; `map' in transform-application, since in the application case we
+;;; know that `(func arg . args)' is an error.  It would probably be
+;;; better for the transform-application case to check for an improper
+;;; list explicitly and signal a more explicit error.
+
+(define (syntax-error x)
+  (error "Syntax error in expression" x))
+
+(define-macro (scheme exp . module)
+  (let ((m (if (null? module)
+              the-root-module
+              (save-module-excursion
+               (lambda ()
+                 ;; In order for `resolve-module' to work as
+                 ;; expected, the current module must contain the
+                 ;; `app' variable.  This is not true for #:pure
+                 ;; modules, specifically (lang elisp base).  So,
+                 ;; switch to the root module (guile) before calling
+                 ;; resolve-module.
+                 (set-current-module the-root-module)
+                 (resolve-module (car module)))))))
+    (let ((x `(,eval (,quote ,exp) ,m)))
+      ;;(write x)
+      ;;(newline)
+      x)))
+
+(define (transformer x)
+  (cond ((pair? x)
+        (cond ((symbol? (car x))
+               (case (car x)
+                 ;; Allow module-related forms through intact.
+                 ((define-module use-modules use-syntax)
+                  x)
+                 ;; Escape to Scheme.
+                 ((scheme)
+                  (cons-source x scheme (cdr x)))
+                 ;; Quoting.
+                 ((quote function)
+                  (cons-source x quote (transform-quote (cdr x))))
+                 ((quasiquote)
+                  (cons-source x quasiquote (transform-quasiquote (cdr x))))
+                 ;; Anything else is a function or macro application.
+                 (else (transform-application x))))
+              ((and (pair? (car x))
+                    (eq? (caar x) 'quasiquote))
+               (transformer (car x)))
+              (else (syntax-error x))))
+       (else
+        (transform-datum x))))
+
+(define (transform-datum x)
+  (cond ((eq? x 'nil) %nil)
+       ((eq? x 't) #t)
+       ;; Could add other translations here, notably `?A' -> 65 etc.
+       (else x)))
+
+(define (transform-quote x)
+  (trc 'transform-quote x)
+  (cond ((not (pair? x))
+        (transform-datum x))
+       (else
+        (cons-source x
+                     (transform-quote (car x))
+                     (transform-quote (cdr x))))))
+
+(define (transform-quasiquote x)
+  (trc 'transform-quasiquote x)
+  (cond ((not (pair? x))
+        (transform-datum x))
+       ((symbol? (car x))
+        (case (car x)
+          ((unquote) (list 'unquote (transformer (cadr x))))
+          ((unquote-splicing) (list 'unquote-splicing (transformer (cadr x))))
+          (else (cons-source x
+                             (transform-datum (car x))
+                             (transform-quasiquote (cdr x))))))
+       (else
+        (cons-source x
+                     (transform-quasiquote (car x))
+                     (transform-quasiquote (cdr x))))))
+
+(define (transform-application x)
+  (cons-source x @fop `(,(car x) (,transformer-macro ,@(map transform-quote (cdr x))))))
+
+(define transformer-macro
+  (procedure->memoizing-macro
+   (let ((cdr cdr))
+     (lambda (exp env)
+       (cons-source exp list (map transformer (cdr exp)))))))
+
+(define transform transformer)