1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-13 15:10:34 +02:00

* Complete Elisp translator work.

This commit is contained in:
Neil Jerram 2002-02-08 11:50:51 +00:00
parent 1f761e0a59
commit e79236a948
19 changed files with 385 additions and 228 deletions

View file

@ -1,3 +1,85 @@
2002-02-08 Neil Jerram <neil@ossau.uklinux.net>
* STATUS: New file.
* README: Updated.
* interface.scm (translate-elisp): New exported procedure.
(elisp-function): Symbol var is `obj', not `symbol'.
* internals/lambda.scm, primitives/fns.scm: Fix confusion between
interactive-spec and interactive-specification.
* internals/lambda.scm (transform-lambda), primitives/syntax.scm
(defmacro): Bind unspecified optional and rest arguments to #nil,
not #f.
* internals/null.scm (->nil, lambda->nil): New, exported.
(null): Use ->nil.
* primitives/features.scm (featurep), primitives/fns.scm
(fboundp, subrp): Use ->nil.
* internals/lists.scm (cons, setcdr, memq, member, assq, assoc):
Simplified.
(car, cdr): Return #nil rather than #f.
* primitives/load.scm (current-load-list), primitives/pure.scm
(purify-flag): Set to #nil, not #f.
* primitives/match.scm (string-match): Return #nil rather than #f.
* primitives/numbers.scm (integerp, numberp),
primitives/strings.scm (string-lessp, stringp): Use lambda->nil.
* primitives/symprop.scm (boundp): Use ->nil.
(symbolp, local-variable-if-set-p): Return #nil rather than #f.
* primitives/syntax.scm (prog1, prog2): Mangle variable names
further to lessen possibility of conflicts.
(if, and, or, cond): Return #nil rather than #f.
(cond): Return #t rather than t (which is undefined).
(let, let*): Bind uninitialized variables to #nil, not #f.
* transform.scm: Resolve inconsistency in usage of `map', and add
an explanatory note. Also cleaned up use of subsidiary
transformation functions. Also use cons-source wherever possible.
(transform-datum, transform-quote): New.
(transform-quasiquote): Renamed from `transform-inside-qq'.
(transform-application): Apply `transform-quote' to application
args.
(cars->nil): Removed.
* internals/null.scm (null), primitives/lists.scm (cons, car, cdr,
setcdr, memq, member, assq, assoc, nth): Update to take into
account new libguile support for Elisp nil value.
2002-02-06 Neil Jerram <neil@ossau.uklinux.net>
* example.el (time): New macro, for performance measurement.
Accompanying comment compares results for Guile and Emacs.
* transform.scm (scheme): New macro.
(transformer): New implementation of `scheme' escape that doesn't
rely on (lang elisp base) importing Guile bindings.
* base.scm: No longer import anything from (guile).
(load-emacs): Add scheme form to ensure that keywords
read option is set correctly.
* primitives/syntax.scm (defmacro, let, let*): Unquote uses of
`@bind' in transformed code.
(if): Unquote uses of `nil-cond' in transformed code.
* internals/lambda.scm (transform-lambda): Unquote use of `@bind'
in transformed code.
* transform.scm (transformer-macro): Don't quote `list' in
transformed code.
(transform-application): Don't quote `@fop' in transformed code.
(transformer): No need to treat `@bind' and `@fop' as special
cases in input to the transformer.
2002-02-04 Neil Jerram <neil@ossau.uklinux.net> 2002-02-04 Neil Jerram <neil@ossau.uklinux.net>
* primitives/syntax.scm (parse-formals, transform-lambda, * primitives/syntax.scm (parse-formals, transform-lambda,

View file

@ -45,8 +45,7 @@ and try to bootstrap a complete Emacs environment:
* Status * Status
Please note that this is work in progress; the translator is Please see the STATUS file for the full position.
incomplete and not yet widely tested.
** Trying to load a complete Emacs environment. ** Trying to load a complete Emacs environment.
@ -163,12 +162,23 @@ transform Elisp variable references after all.
*** Truth value stuff *** Truth value stuff
Lots of stuff to do with providing the special self-evaluating `nil' Following extensive discussions on the Guile mailing list between
and `t' symbols, and macros that convert between Scheme and Elisp September 2001 and January 2002, we decided to go with Jim Blandy's
truth values, and so on. proposal. See devel/translation/lisp-and-scheme.text for details.
I'm hoping that most of this will go away, but I need to show that - The Elisp nil value is a new immediate SCM_MAKIFLAG, eq?-distinct
it's feasible first. from both #f and '() (and of course any other Scheme value). It can
be accessed via the (guile) binding `%nil', and prints as `#nil'.
- All Elisp primitives treat #nil, #f and '() as identical.
- Scheme truth-testing primitives have been modified so that they
treat #nil the same as #f.
- Scheme list-manipulating primitives have been modified so that they
treat #nil the same as '().
- The Elisp t value is the same as #t.
** Emacs editing primitives ** Emacs editing primitives
@ -191,8 +201,9 @@ that Ken Raeburn has been doing on the Emacs codebase.
Elisp is close enough to Scheme that it's convenient to coopt the Elisp is close enough to Scheme that it's convenient to coopt the
existing Guile reader rather than to write a new one from scratch, but existing Guile reader rather than to write a new one from scratch, but
there are a few syntactic differences that will require adding Elisp there are a few syntactic differences that will require changes in
support to the reader. reading and printing. None of the following changes has yet been
implemented.
- Character syntax is `?a' rather than `#\a'. (Not done. More - Character syntax is `?a' rather than `#\a'. (Not done. More
precisely, `?a' in Elisp isn't character syntax but an alternative precisely, `?a' in Elisp isn't character syntax but an alternative
@ -204,12 +215,10 @@ support to the reader.
and so on.) and so on.)
- `nil' and `t' should be read (I think) as #f and #t. (Done.) - Vector syntax is `[1 2 3]' rather than `#(1 2 3)'.
- Vector syntax is `[1 2 3]' rather than `#(1 2 3)'. (Not done.) - When in an Elisp environment, #nil and #t should print as `nil' and
`t'.
Correspondingly, when printing, #f and '() should be written as
`nil'. (Not done.)
** The Elisp evaluation module (lang elisp base) ** The Elisp evaluation module (lang elisp base)
@ -272,36 +281,6 @@ worry about adding unexec support to Guile!) For the output that
currently results from calling `(load-emacs)', see above in the Status currently results from calling `(load-emacs)', see above in the Status
section. section.
* nil, #f and '()
For Jim Blandy's notes on this, see the reference at the bottom of
this file. Currently I'm investigating a different approach, which is
better IMO than Jim's proposal because it avoids requiring multiple
false values in the Scheme world.
According to my approach...
- `nil' and `t' are read (when in Elisp mode) as #f and #t.
- `(if x ...)', `(while x ...)' etc. are translated to something
like `(if (and x (not (null? x))) ...)'.
- Functions which interpret an argument as a list --
`cons', `setcdr', `memq', etc. -- either convert #f to '(), or
handle the #f case specially.
- `eq' treats #f and '() as the same.
- Optionally, functions which produce '() values -- i.e. the reader
and `cdr' -- could convert those immediately to #f. This shouldn't
affect the validity of any Elisp code, but it alters the balance of
#f and '() values swimming around in that code and so affects what
happens if two such values are returned to the Scheme world and then
compared. However, since you can never completely solve this
problem (unless you are prepared to convert arbitrarily deep
structures on entry to the Elisp world, which would kill performance),
I'm inclined not to try to solve it at all.
* Resources * Resources
** Ken Raeburn's Guile Emacs page ** Ken Raeburn's Guile Emacs page
@ -316,6 +295,9 @@ http://gemacs.sourceforge.net
http://sanpietro.red-bean.com/guile/guile/old/3114.html http://sanpietro.red-bean.com/guile/guile/old/3114.html
Also now stored as guile-core/devel/translation/lisp-and-scheme.text
in Guile CVS.
** Mikael Djurfeldt's notes on translation ** Mikael Djurfeldt's notes on translation
See file guile-cvs/devel/translation/langtools.text in Guile CVS. See file guile-core/devel/translation/langtools.text in Guile CVS.

35
lang/elisp/STATUS Normal file
View file

@ -0,0 +1,35 @@
-*-text-*-
I've now finished my currently planned work on the Emacs Lisp
translator in guile-core CVS.
It works well enough for experimentation and playing around with --
see the README file for details of what it _can_ do -- but has two
serious restrictions:
- Most Emacs Lisp primitives are not yet implemented. In particular,
there are no buffer-related primitives.
- Performance compares badly with Emacs. Using a handful of
completely unscientific tests, I found that Guile was between 2 and
20 times slower than Emacs. (See the comment in
lang/elisp/example.el for details of tests and results.)
Interestingly, both these restrictions point in the same direction:
the way forward is to define the primitives by compiling a
preprocessed version of the Emacs source code, not by trying to
implement them in Scheme. (Which, of course, is what Ken Raeburn's
project is already trying to do.)
Given this conclusion, I expect that most of the translator's Scheme
code will eventually become obsolete, replaced by bits of Emacs C
code. Until then, though, it should have a role:
- as a guide to the Guile Emacs project on how to interface to the
Elisp support in libguile (notably, usage of `@fop' and `@bind')
- as a proof of concept and fun thing to experiment with
- as a working translator that could help us develop our picture of
how we want to integrate translator usage in general with the rest
of Guile.

View file

@ -1,13 +1,12 @@
(define-module (lang elisp base) (define-module (lang elisp base)
;; Be pure. Nothing in this module requires most of the standard ;; Be pure. Nothing in this module requires symbols that map to the
;; Guile builtins, and it creates a problem if this module has ;; standard Guile builtins, and it creates a problem if this module
;; access to them, as @bind can dynamically change their values. ;; has access to them, as @bind can dynamically change their values.
;; Transformer output always uses the values of builtin procedures
;; and macros directly.
#:pure #:pure
;; But we do need a few builtins - import them here.
#:use-module ((guile) #:select (@fop @bind nil-cond))
;; {Elisp Primitives} ;; {Elisp Primitives}
;; ;;
;; In other words, Scheme definitions of elisp primitives. This ;; In other words, Scheme definitions of elisp primitives. This
@ -34,13 +33,10 @@
;; Now switch into Emacs Lisp syntax. ;; Now switch into Emacs Lisp syntax.
#:use-syntax (lang elisp transform)) #:use-syntax (lang elisp transform))
;(use-modules (lang elisp transform))
;(read-set! keywords 'prefix)
;(set-module-transformer! (current-module) transformer)
;;; Everything below here is written in Elisp. ;;; Everything below here is written in Elisp.
(defun load-emacs () (defun load-emacs ()
(scheme (read-set! keywords 'prefix))
(message "Calling loadup.el to clothe the bare Emacs...") (message "Calling loadup.el to clothe the bare Emacs...")
(load "loadup.el") (load "loadup.el")
(message "Guile Emacs now fully clothed")) (message "Guile Emacs now fully clothed"))

View file

@ -8,3 +8,32 @@
(apply 'concat contents) (apply 'concat contents)
"</BODY>\n" "</BODY>\n"
"</HTML>\n")) "</HTML>\n"))
(defmacro time (repeat-count &rest body)
`(let ((count ,repeat-count)
(beg (current-time))
end)
(while (> count 0)
(setq count (- count 1))
,@body)
(setq end (current-time))
(+ (* 1000000.0 (+ (* 65536.0 (- (car end) (car beg)))
(- (cadr end) (cadr beg))))
(* 1.0 (- (caddr end) (caddr beg))))))
;Non-scientific performance measurements (Guile measurements are with
;`guile -q --no-debug'):
;
;(time 100000 (+ 3 4))
; => 225,071 (Emacs) 4,000,000 (Guile)
;(time 100000 (lambda () 1))
; => 2,410,456 (Emacs) 4,000,000 (Guile)
;(time 100000 (apply 'concat (mapcar (lambda (s) (concat s "." s)) '("a" "b" "c" "d"))))
; => 10,185,792 (Emacs) 136,000,000 (Guile)
;(defun sc (s) (concat s "." s))
;(time 100000 (apply 'concat (mapcar 'sc '("a" "b" "c" "d"))))
; => 7,870,055 (Emacs) 26,700,000 (Guile)
;
;Sadly, it looks like the translator's performance sucks quite badly
;when compared with Emacs. But the translator is still very new, so
;there's probably plenty of room of improvement.

View file

@ -2,7 +2,9 @@
#:use-module (lang elisp internals evaluation) #:use-module (lang elisp internals evaluation)
#:use-module (lang elisp internals fset) #:use-module (lang elisp internals fset)
#:use-module ((lang elisp internals load) #:select ((load . elisp:load))) #:use-module ((lang elisp internals load) #:select ((load . elisp:load)))
#:use-module ((lang elisp transform) #:select (transformer))
#:export (eval-elisp #:export (eval-elisp
translate-elisp
elisp-function elisp-function
elisp-variable elisp-variable
load-elisp-file load-elisp-file
@ -19,6 +21,10 @@
"Evaluate the Elisp expression @var{x}." "Evaluate the Elisp expression @var{x}."
(eval x the-elisp-module)) (eval x the-elisp-module))
(define (translate-elisp x)
"Translate the Elisp expression @var{x} to equivalent Scheme code."
(transformer x))
(define (elisp-function sym) (define (elisp-function sym)
"Return the procedure or macro that implements @var{sym} in Elisp. "Return the procedure or macro that implements @var{sym} in Elisp.
If @var{sym} has no Elisp function definition, return @code{#f}." If @var{sym} has no Elisp function definition, return @code{#f}."
@ -112,7 +118,7 @@ exported to Elisp."
(error "No macro name specified or deducible:" obj))) (error "No macro name specified or deducible:" obj)))
((symbol? obj) ((symbol? obj)
(or name (or name
(set! name symbol)) (set! name obj))
(module-add! the-elisp-module name (module-add! the-elisp-module name
(module-ref (current-module) obj))) (module-ref (current-module) obj)))
(else (else

View file

@ -67,28 +67,28 @@
`(((,> %--num-args ,(+ num-required num-optional)) `(((,> %--num-args ,(+ num-required num-optional))
(,error "Wrong number of args (too many args)")))) (,error "Wrong number of args (too many args)"))))
(else (else
(@bind ,(append (map (lambda (i) (, @bind ,(append (map (lambda (i)
(list (list-ref required i) (list (list-ref required i)
`(,list-ref %--args ,i))) `(,list-ref %--args ,i)))
(iota num-required)) (iota num-required))
(map (lambda (i) (map (lambda (i)
(let ((i+nr (+ i num-required))) (let ((i+nr (+ i num-required)))
(list (list-ref optional i) (list (list-ref optional i)
`(,if (,> %--num-args ,i+nr) `(,if (,> %--num-args ,i+nr)
(,list-ref %--args ,i+nr) (,list-ref %--args ,i+nr)
#f)))) ,%nil))))
(iota num-optional)) (iota num-optional))
(if rest (if rest
(list (list rest (list (list rest
`(,if (,> %--num-args `(,if (,> %--num-args
,(+ num-required ,(+ num-required
num-optional)) num-optional))
(,list-tail %--args (,list-tail %--args
,(+ num-required ,(+ num-required
num-optional)) num-optional))
'()))) ,%nil)))
'())) '()))
,@(map transformer (cddr exp))))))))))) ,@(map transformer (cddr exp)))))))))))
(define (set-not-subr! proc boolean) (define (set-not-subr! proc boolean)
(set! (not-subr? proc) boolean)) (set! (not-subr? proc) boolean))
@ -101,7 +101,7 @@
(,set-procedure-property! %--lambda (,quote name) (,quote ,name)) (,set-procedure-property! %--lambda (,quote name) (,quote ,name))
(,set-not-subr! %--lambda #t) (,set-not-subr! %--lambda #t)
,@(if is ,@(if is
`((,set! (,interactive-spec %--lambda) (,quote ,is))) `((,set! (,interactive-specification %--lambda) (,quote ,is)))
'()) '())
%--lambda))) %--lambda)))

View file

@ -1,7 +1,13 @@
(define-module (lang elisp internals null) (define-module (lang elisp internals null)
#:export (null)) #:export (->nil lambda->nil null))
(define (->nil x)
(or x %nil))
(define (lambda->nil proc)
(lambda args
(->nil (apply proc args))))
(define (null obj) (define (null obj)
(or (not obj) (->nil (or (not obj)
(null? obj) (null? obj))))
(eq? obj 'nil))) ; Should be removed.

View file

@ -1,6 +1,7 @@
(define-module (lang elisp primitives features) (define-module (lang elisp primitives features)
#:use-module (lang elisp internals fset) #:use-module (lang elisp internals fset)
#:use-module (lang elisp internals load) #:use-module (lang elisp internals load)
#:use-module (lang elisp internals null)
#:use-module (ice-9 optargs)) #:use-module (ice-9 optargs))
(define-public features '()) (define-public features '())
@ -12,7 +13,7 @@
(fset 'featurep (fset 'featurep
(lambda (feature) (lambda (feature)
(memq feature features))) (->nil (memq feature features))))
(fset 'require (fset 'require
(lambda* (feature #:optional file-name noerror) (lambda* (feature #:optional file-name noerror)

View file

@ -18,11 +18,11 @@
(fset 'commandp (fset 'commandp
(lambda (sym) (lambda (sym)
(if (interactive-spec (fref sym)) #t %nil))) (if (interactive-specification (fref sym)) #t %nil)))
(fset 'fboundp (fset 'fboundp
(lambda (sym) (lambda (sym)
(variable? (symbol-fref sym)))) (->nil (variable? (symbol-fref sym)))))
(fset 'symbol-function fref/error-if-void) (fset 'symbol-function fref/error-if-void)
@ -30,7 +30,7 @@
(fset 'subrp (fset 'subrp
(lambda (obj) (lambda (obj)
(not (not-subr? obj)))) (->nil (not (not-subr? obj)))))
(fset 'byte-code-function-p (fset 'byte-code-function-p
(lambda (object) (lambda (object)

View file

@ -3,9 +3,7 @@
#:use-module (lang elisp internals null) #:use-module (lang elisp internals null)
#:use-module (lang elisp internals signal)) #:use-module (lang elisp internals signal))
(fset 'cons (fset 'cons cons)
(lambda (x y)
(cons x (or y '()))))
(fset 'null null) (fset 'null null)
@ -14,13 +12,13 @@
(fset 'car (fset 'car
(lambda (l) (lambda (l)
(if (null l) (if (null l)
#f %nil
(car l)))) (car l))))
(fset 'cdr (fset 'cdr
(lambda (l) (lambda (l)
(if (null l) (if (null l)
#f %nil
(cdr l)))) (cdr l))))
(fset 'eq (fset 'eq
@ -35,12 +33,7 @@
(fset 'setcar set-car!) (fset 'setcar set-car!)
(fset 'setcdr (fset 'setcdr set-cdr!)
(lambda (cell newcdr)
(set-cdr! cell
(if (null newcdr)
'()
newcdr))))
(for-each (lambda (sym proc) (for-each (lambda (sym proc)
(fset sym (fset sym
@ -48,14 +41,10 @@
(if (null list) (if (null list)
%nil %nil
(if (null elt) (if (null elt)
(or (proc #f list) (let loop ((l list))
(proc '() list) (cond ((null l) %nil)
(proc %nil list) ((null (car l)) l)
(proc 'nil list)) ; 'nil shouldn't be (else (loop (cdr l)))))
; here, as it should
; have been
; translated by the
; transformer.
(proc elt list)))))) (proc elt list))))))
'( memq member assq assoc) '( memq member assq assoc)
`(,memq ,member ,assq ,assoc)) `(,memq ,member ,assq ,assoc))
@ -97,7 +86,7 @@
(lambda (n list) (lambda (n list)
(if (or (null list) (if (or (null list)
(>= n (length list))) (>= n (length list)))
#f %nil
(list-ref list n)))) (list-ref list n))))
(fset 'listp (fset 'listp

View file

@ -14,4 +14,4 @@
(lambda args (lambda args
#t)) #t))
(define-public current-load-list #f) (define-public current-load-list %nil)

View file

@ -45,7 +45,7 @@
(iota (match:count match)))) (iota (match:count match))))
#f))) #f)))
(if last-match (car last-match) #f))) (if last-match (car last-match) %nil)))
(fset 'match-beginning (fset 'match-beginning
(lambda (subexp) (lambda (subexp)

View file

@ -1,9 +1,10 @@
(define-module (lang elisp primitives numbers) (define-module (lang elisp primitives numbers)
#:use-module (lang elisp internals fset)) #:use-module (lang elisp internals fset)
#:use-module (lang elisp internals null))
(fset 'logior logior) (fset 'logior logior)
(fset 'logand logand) (fset 'logand logand)
(fset 'integerp integer?) (fset 'integerp (lambda->nil integer?))
(fset '= =) (fset '= =)
(fset '< <) (fset '< <)
(fset '> >) (fset '> >)
@ -39,4 +40,4 @@
(- shift 1)))))) (- shift 1))))))
lsh)) lsh))
(fset 'numberp number?) (fset 'numberp (lambda->nil number?))

View file

@ -5,4 +5,4 @@
(fset 'purecopy identity) (fset 'purecopy identity)
(define-public purify-flag #f) (define-public purify-flag %nil)

View file

@ -1,5 +1,6 @@
(define-module (lang elisp primitives strings) (define-module (lang elisp primitives strings)
#:use-module (lang elisp internals fset) #:use-module (lang elisp internals fset)
#:use-module (lang elisp internals null)
#:use-module (lang elisp internals signal)) #:use-module (lang elisp internals signal))
(fset 'substring substring) (fset 'substring substring)
@ -19,7 +20,7 @@
(fset 'number-to-string number->string) (fset 'number-to-string number->string)
(fset 'string-lessp string<?) (fset 'string-lessp (lambda->nil string<?))
(fset 'string< 'string-lessp) (fset 'string< 'string-lessp)
(fset 'aref (fset 'aref
@ -28,6 +29,6 @@
((string? array) (char->integer (string-ref array idx))) ((string? array) (char->integer (string-ref array idx)))
(else (wta 'arrayp array 1))))) (else (wta 'arrayp array 1)))))
(fset 'stringp string?) (fset 'stringp (lambda->nil string?))
(fset 'vector vector) (fset 'vector vector)

View file

@ -1,7 +1,8 @@
(define-module (lang elisp primitives symprop) (define-module (lang elisp primitives symprop)
#:use-module (lang elisp internals set)
#:use-module (lang elisp internals fset)
#:use-module (lang elisp internals evaluation) #:use-module (lang elisp internals evaluation)
#:use-module (lang elisp internals fset)
#:use-module (lang elisp internals null)
#:use-module (lang elisp internals set)
#:use-module (ice-9 optargs)) #:use-module (ice-9 optargs))
;;; {Elisp Exports} ;;; {Elisp Exports}
@ -16,7 +17,7 @@
(fset 'boundp (fset 'boundp
(lambda (sym) (lambda (sym)
(module-defined? the-elisp-module sym))) (->nil (module-defined? the-elisp-module sym))))
(fset 'default-boundp 'boundp) (fset 'default-boundp 'boundp)
@ -29,10 +30,11 @@
(fset 'symbolp (fset 'symbolp
(lambda (object) (lambda (object)
(or (symbol? object) (or (symbol? object)
(keyword? object)))) (keyword? object)
%nil)))
(fset 'local-variable-if-set-p (fset 'local-variable-if-set-p
(lambda* (variable #:optional buffer) (lambda* (variable #:optional buffer)
#f)) %nil))
(fset 'symbol-name symbol->string) (fset 'symbol-name symbol->string)

View file

@ -32,7 +32,6 @@
`(,quote ,(cadr exp)) `(,quote ,(cadr exp))
`(,begin (,if (,not (,defined? (,quote ,(cadr exp)))) `(,begin (,if (,not (,defined? (,quote ,(cadr exp))))
,(setq (list (car exp) (cadr exp) (caddr exp)) env)) ,(setq (list (car exp) (cadr exp) (caddr exp)) env))
;; (,macro-setq ,(cadr exp) ,(caddr exp)))
(,quote ,(cadr exp))))))) (,quote ,(cadr exp)))))))
(fset 'defconst (fset 'defconst
@ -87,28 +86,28 @@
`(((,> %--num-args ,(+ num-required num-optional)) `(((,> %--num-args ,(+ num-required num-optional))
(,error "Wrong number of args (too many args)")))) (,error "Wrong number of args (too many args)"))))
(else (,transformer (else (,transformer
(@bind ,(append (map (lambda (i) (, @bind ,(append (map (lambda (i)
(list (list-ref required i) (list (list-ref required i)
`(,list-ref %--args ,i))) `(,list-ref %--args ,i)))
(iota num-required)) (iota num-required))
(map (lambda (i) (map (lambda (i)
(let ((i+nr (+ i num-required))) (let ((i+nr (+ i num-required)))
(list (list-ref optional i) (list (list-ref optional i)
`(,if (,> %--num-args ,i+nr) `(,if (,> %--num-args ,i+nr)
(,list-ref %--args ,i+nr) (,list-ref %--args ,i+nr)
#f)))) ,%nil))))
(iota num-optional)) (iota num-optional))
(if rest (if rest
(list (list rest (list (list rest
`(,if (,> %--num-args `(,if (,> %--num-args
,(+ num-required ,(+ num-required
num-optional)) num-optional))
(,list-tail %--args (,list-tail %--args
,(+ num-required ,(+ num-required
num-optional)) num-optional))
'()))) ,%nil)))
'())) '()))
,@(map transformer (cdddr exp))))))))))))))))) ,@(map transformer (cdddr exp)))))))))))))))))
;;; {Sequencing} ;;; {Sequencing}
@ -120,36 +119,34 @@
(fset 'prog1 (fset 'prog1
(procedure->memoizing-macro (procedure->memoizing-macro
(lambda (exp env) (lambda (exp env)
`(,let ((%res1 ,(transformer (cadr exp)))) `(,let ((%--res1 ,(transformer (cadr exp))))
,@(map transformer (cddr exp)) ,@(map transformer (cddr exp))
%res1)))) %--res1))))
(fset 'prog2 (fset 'prog2
(procedure->memoizing-macro (procedure->memoizing-macro
(lambda (exp env) (lambda (exp env)
`(,begin ,(transformer (cadr exp)) `(,begin ,(transformer (cadr exp))
(,let ((%res2 ,(transformer (caddr exp)))) (,let ((%--res2 ,(transformer (caddr exp))))
,@(map transformer (cdddr exp)) ,@(map transformer (cdddr exp))
%res2))))) %--res2)))))
;;; {Conditionals} ;;; {Conditionals}
(define <-- *unspecified*)
(fset 'if (fset 'if
(procedure->memoizing-macro (procedure->memoizing-macro
(lambda (exp env) (lambda (exp env)
(let ((else-case (cdddr exp))) (let ((else-case (cdddr exp)))
(cond ((null? else-case) (cond ((null? else-case)
`(nil-cond ,(transformer (cadr exp)) ,(transformer (caddr exp)) #f)) `(,nil-cond ,(transformer (cadr exp)) ,(transformer (caddr exp)) ,%nil))
((null? (cdr else-case)) ((null? (cdr else-case))
`(nil-cond ,(transformer (cadr exp)) `(,nil-cond ,(transformer (cadr exp))
,(transformer (caddr exp)) ,(transformer (caddr exp))
,(transformer (car else-case)))) ,(transformer (car else-case))))
(else (else
`(nil-cond ,(transformer (cadr exp)) `(,nil-cond ,(transformer (cadr exp))
,(transformer (caddr exp)) ,(transformer (caddr exp))
(,begin ,@(map transformer else-case))))))))) (,begin ,@(map transformer else-case)))))))))
(fset 'and (fset 'and
(procedure->memoizing-macro (procedure->memoizing-macro
@ -162,13 +159,26 @@
(if (null? (cdr args)) (if (null? (cdr args))
(list (transformer (car args))) (list (transformer (car args)))
(cons (list not (transformer (car args))) (cons (list not (transformer (car args)))
(cons #f (cons %nil
(loop (cdr args)))))))))))) (loop (cdr args))))))))))))
;;; NIL-COND expressions have the form:
;;;
;;; (nil-cond COND VAL COND VAL ... ELSEVAL)
;;;
;;; The CONDs are evaluated in order until one of them returns true
;;; (in the Elisp sense, so not including empty lists). If a COND
;;; returns true, its corresponding VAL is evaluated and returned,
;;; except if that VAL is the unspecified value, in which case the
;;; result of evaluating the COND is returned. If none of the COND's
;;; returns true, ELSEVAL is evaluated and its value returned.
(define <-- *unspecified*)
(fset 'or (fset 'or
(procedure->memoizing-macro (procedure->memoizing-macro
(lambda (exp env) (lambda (exp env)
(cond ((null? (cdr exp)) #f) (cond ((null? (cdr exp)) %nil)
((null? (cddr exp)) (transformer (cadr exp))) ((null? (cddr exp)) (transformer (cadr exp)))
(else (else
(cons nil-cond (cons nil-cond
@ -183,15 +193,15 @@
(procedure->memoizing-macro (procedure->memoizing-macro
(lambda (exp env) (lambda (exp env)
(if (null? (cdr exp)) (if (null? (cdr exp))
#f %nil
(cons (cons
nil-cond nil-cond
(let loop ((clauses (cdr exp))) (let loop ((clauses (cdr exp)))
(if (null? clauses) (if (null? clauses)
'(#f) (list %nil)
(let ((clause (car clauses))) (let ((clause (car clauses)))
(if (eq? (car clause) #t) (if (eq? (car clause) #t)
(cond ((null? (cdr clause)) '(t)) (cond ((null? (cdr clause)) (list #t))
((null? (cddr clause)) ((null? (cddr clause))
(list (transformer (cadr clause)))) (list (transformer (cadr clause))))
(else `((,begin ,@(map transformer (cdr clause)))))) (else `((,begin ,@(map transformer (cdr clause))))))
@ -210,7 +220,7 @@
(,nil-cond ,(transformer (cadr exp)) (,nil-cond ,(transformer (cadr exp))
(,begin ,@(map transformer (cddr exp)) (,begin ,@(map transformer (cddr exp))
(%--while)) (%--while))
#f)))) ,%nil))))
%--while))))) %--while)))))
;;; {Local binding} ;;; {Local binding}
@ -218,13 +228,13 @@
(fset 'let (fset 'let
(procedure->memoizing-macro (procedure->memoizing-macro
(lambda (exp env) (lambda (exp env)
`(@bind ,(map (lambda (binding) `(, @bind ,(map (lambda (binding)
(trc 'let binding) (trc 'let binding)
(if (pair? binding) (if (pair? binding)
`(,(car binding) ,(transformer (cadr binding))) `(,(car binding) ,(transformer (cadr binding)))
`(,binding #f))) `(,binding ,%nil)))
(cadr exp)) (cadr exp))
,@(map transformer (cddr exp)))))) ,@(map transformer (cddr exp))))))
(fset 'let* (fset 'let*
(procedure->memoizing-macro (procedure->memoizing-macro
@ -234,11 +244,11 @@
(car (let loop ((bindings (cadr exp))) (car (let loop ((bindings (cadr exp)))
(if (null? bindings) (if (null? bindings)
(map transformer (cddr exp)) (map transformer (cddr exp))
`((@bind (,(let ((binding (car bindings))) `((, @bind (,(let ((binding (car bindings)))
(if (pair? binding) (if (pair? binding)
`(,(car binding) ,(transformer (cadr binding))) `(,(car binding) ,(transformer (cadr binding)))
`(,binding #f)))) `(,binding ,%nil))))
,@(loop (cdr bindings))))))))))) ,@(loop (cdr bindings)))))))))))
;;; {Exception handling} ;;; {Exception handling}

View file

@ -5,81 +5,98 @@
#:use-module (ice-9 session) #:use-module (ice-9 session)
#:export (transformer transform)) #:export (transformer transform))
;;; {S-expressions} ;;; 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) (define (syntax-error x)
(error "Syntax error in expression" x)) (error "Syntax error in expression" x))
;; Should be made mutating instead of constructing (define-macro (scheme exp . module)
;; (let ((m (resolve-module (if (null? module)
'(guile-user)
(car module)))))
(let ((x `(,eval (,quote ,exp) ,m)))
(write x)
(newline)
x)))
(define (transformer 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) (cond ((eq? x 'nil) %nil)
((eq? x 't) #t) ((eq? x 't) #t)
((null? x) %nil) ;; Could add other translations here, notably `?A' -> 65 etc.
((not (pair? x)) x) (else 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) (define (transform-quote x)
(cons quasiquote (trc 'transform-quote x)
(map transform-inside-qq (cdr exp)))) (cond ((not (pair? x))
(transform-datum x))
(else
(cons-source x
(transform-quote (car x))
(transform-quote (cdr x))))))
(define (transform-inside-qq x) (define (transform-quasiquote x)
(trc 'transform-inside-qq x) (trc 'transform-quasiquote x)
(cond ((not (pair? x)) x) (cond ((not (pair? x))
(transform-datum x))
((symbol? (car x)) ((symbol? (car x))
(case (car x) (case (car x)
((unquote) (list 'unquote (transformer (cadr x)))) ((unquote) (list 'unquote (transformer (cadr x))))
((unquote-splicing) (list 'unquote-splicing (transformer (cadr x)))) ((unquote-splicing) (list 'unquote-splicing (transformer (cadr x))))
(else (cons (car x) (map transform-inside-qq (cdr x)))))) (else (cons-source x
(transform-datum (car x))
(transform-quasiquote (cdr x))))))
(else (else
(cons (transform-inside-qq (car x)) (transform-inside-qq (cdr x)))))) (cons-source x
(transform-quasiquote (car x))
(transform-quasiquote (cdr x))))))
(define (transform-application x) (define (transform-application x)
(cons-source x (cons-source x @fop `(,(car x) (,transformer-macro ,@(map transform-quote (cdr x))))))
'@fop
`(,(car x) (,transformer-macro ,@(cdr x)))))
(define transformer-macro (define transformer-macro
(procedure->memoizing-macro (procedure->memoizing-macro
(let ((cdr cdr)) (let ((cdr cdr))
(lambda (exp env) (lambda (exp env)
(cons 'list (map transformer (cdr exp))))))) (cons-source exp 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 transform transformer)