1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-11 14:21:10 +02:00

Implement some elisp constructs in macros instead of hard-coded compiler code.

* module/language/elisp/compile-tree-il.scm: Remove implementation of prog1,
  and, or, cond, dolist.
* module/language/elisp/runtime/macro-slot.scm: Implement them here instead.
This commit is contained in:
Daniel Kraft 2009-07-30 21:43:24 +02:00
parent ce305387df
commit e6042c08b7
2 changed files with 99 additions and 115 deletions

View file

@ -504,45 +504,6 @@
(make-const loc expr))) (make-const loc expr)))
; Compile a dolist construct.
; This is compiled to something along:
; (with-fluid* iter-var %nil
; (lambda ()
; (let iterate ((tail list))
; (if (null? tail)
; result
; (begin
; (fluid-set! iter-var (car tail))
; body
; (iterate (cdr tail)))))))
(define (compile-dolist loc var iter-list result body)
(let* ((tailvar (gensym))
(iterate (gensym))
(tailref (make-lexical-ref loc tailvar tailvar))
(iterate-func (make-lambda loc `(,tailvar) `(,tailvar) '()
(make-conditional loc
(call-primitive loc 'null? tailref)
(compile-expr result)
(make-sequence loc
`(,(set-variable! loc var value-slot
(call-primitive loc 'car tailref))
,@(map compile-expr body)
,(make-application loc
(make-lexical-ref loc iterate iterate)
(list (call-primitive loc 'cdr
tailref)))))))))
(mark-fluid-needed! (fluid-ref bindings-data) var value-slot)
(call-primitive loc 'with-fluid*
(make-module-ref loc value-slot var #t)
(nil-value loc)
(make-lambda loc '() '() '()
(make-letrec loc `(,iterate) `(,iterate) `(,iterate-func)
(make-application loc
(make-lexical-ref loc iterate iterate)
(list (compile-expr iter-list))))))))
; Compile a symbol expression. This is a variable reference or maybe some ; Compile a symbol expression. This is a variable reference or maybe some
; special value like nil. ; special value like nil.
@ -561,16 +522,6 @@
((progn . ,forms) ((progn . ,forms)
(make-sequence loc (map compile-expr forms))) (make-sequence loc (map compile-expr forms)))
; I chose to implement prog1 directly (not with macros) so that the
; temporary variable used can be a lexical one that is not backed by a fluid
; for better performance.
((prog1 ,form1 . ,forms)
(let ((temp (gensym)))
(make-let loc `(,temp) `(,temp) `(,(compile-expr form1))
(make-sequence loc
(append (map compile-expr forms)
(list (make-lexical-ref loc temp temp)))))))
((if ,condition ,ifclause) ((if ,condition ,ifclause)
(make-conditional loc (compile-expr condition) (make-conditional loc (compile-expr condition)
(compile-expr ifclause) (compile-expr ifclause)
@ -584,51 +535,8 @@
(compile-expr ifclause) (compile-expr ifclause)
(make-sequence loc (map compile-expr elses)))) (make-sequence loc (map compile-expr elses))))
; For (cond ...) forms, a special case is a (condition) clause without ; defconst and defvar are kept here in the compiler (rather than doing them
; body. In this case, the value of condition itself should be returned, ; as macros) for if we may want to handle the docstring somehow.
; and thus is saved in a local variable for testing and returning, if it
; is found true.
((cond . ,clauses) (guard (and-map (lambda (el)
(and (list? el) (not (null? el))))
clauses))
(let iterate ((tail clauses))
(if (null? tail)
(nil-value loc)
(let ((cur (car tail)))
(if (null? (cdr cur))
(let ((var (gensym)))
(make-let loc
'(condition) `(,var) `(,(compile-expr (car cur)))
(make-conditional loc
(make-lexical-ref loc 'condition var)
(make-lexical-ref loc 'condition var)
(iterate (cdr tail)))))
(make-conditional loc
(compile-expr (car cur))
(make-sequence loc (map compile-expr (cdr cur)))
(iterate (cdr tail))))))))
((and) (t-value loc))
((and . ,expressions)
(let iterate ((tail expressions))
(if (null? (cdr tail))
(compile-expr (car tail))
(make-conditional loc
(compile-expr (car tail))
(iterate (cdr tail))
(nil-value loc)))))
((or . ,expressions)
(let iterate ((tail expressions))
(if (null? tail)
(nil-value loc)
(let ((var (gensym)))
(make-let loc
'(condition) `(,var) `(,(compile-expr (car tail)))
(make-conditional loc
(make-lexical-ref loc 'condition var)
(make-lexical-ref loc 'condition var)
(iterate (cdr tail))))))))
((defconst ,sym ,value . ,doc) ((defconst ,sym ,value . ,doc)
(if (handle-var-def loc sym doc) (if (handle-var-def loc sym doc)
@ -754,13 +662,6 @@
(make-letrec loc '(iterate) (list itersym) (list iter-thunk) (make-letrec loc '(iterate) (list itersym) (list iter-thunk)
iter-call))) iter-call)))
; dolist is treated here rather than as macro because it can take advantage
; of a non-fluid-based variable.
((dolist (,var ,iter-list) . ,body) (guard (symbol? var))
(compile-dolist loc var iter-list 'nil body))
((dolist (,var ,iter-list ,result) . ,body) (guard (symbol? var))
(compile-dolist loc var iter-list result body))
; catch and throw can mainly be implemented directly using Guile's ; catch and throw can mainly be implemented directly using Guile's
; primitives for exceptions, the only difficulty is that the keys used ; primitives for exceptions, the only difficulty is that the keys used
; within Guile must be symbols, while elisp allows any value and checks ; within Guile must be symbols, while elisp allows any value and checks
@ -768,6 +669,9 @@
; for the Guile primitives and check for matches inside the handler; if ; for the Guile primitives and check for matches inside the handler; if
; the elisp keys are not eq?, we rethrow the exception. ; the elisp keys are not eq?, we rethrow the exception.
; ;
; TODO: Implement catch with a macro once we can build the lambda with
; lexical arguments.
;
; throw is implemented as built-in function. ; throw is implemented as built-in function.
((catch ,tag . ,body) (guard (not (null? body))) ((catch ,tag . ,body) (guard (not (null? body)))
@ -794,6 +698,8 @@
; unwind-protect is just some weaker construct as dynamic-wind, so ; unwind-protect is just some weaker construct as dynamic-wind, so
; straight-forward to implement. ; straight-forward to implement.
; TODO: This might be implemented as a macro, once lambda's without
; arguments do not call with-fluids* anymore.
((unwind-protect ,body . ,clean-ups) (guard (not (null? clean-ups))) ((unwind-protect ,body . ,clean-ups) (guard (not (null? clean-ups)))
(call-primitive loc 'dynamic-wind (call-primitive loc 'dynamic-wind
(make-lambda loc '() '() '() (make-void loc)) (make-lambda loc '() '() '() (make-void loc))
@ -811,6 +717,8 @@
(compile-lambda loc args body)) (compile-lambda loc args body))
; Build a lambda and also assign it to the function cell of some symbol. ; Build a lambda and also assign it to the function cell of some symbol.
; This is no macro as we might want to honour the docstring at some time;
; just as with defvar/defconst.
((defun ,name ,args . ,body) ((defun ,name ,args . ,body)
(if (not (symbol? name)) (if (not (symbol? name))
(error "expected symbol as function name" name) (error "expected symbol as function name" name)
@ -831,6 +739,7 @@
(define-macro! loc name object) (define-macro! loc name object)
(make-const loc name)))) (make-const loc name))))
; XXX: Maybe we could implement backquotes in macros, too.
((,backq ,val) (guard (backquote? backq)) ((,backq ,val) (guard (backquote? backq))
(process-backquote loc val)) (process-backquote loc val))

View file

@ -28,8 +28,16 @@
; here. ; here.
; The prog2 construct can be directly defined in terms of prog1 and progn, ; The prog1 and prog2 constructs can easily be defined as macros using progn
; so this is done using a macro. ; and some lexical-let's to save the intermediate value to return at the end.
(built-in-macro prog1
(lambda (form1 . rest)
(let ((temp (gensym)))
`(without-void-checks (,temp)
(lexical-let ((,temp ,form1))
,@rest
,temp)))))
(built-in-macro prog2 (built-in-macro prog2
(lambda (form1 form2 . rest) (lambda (form1 form2 . rest)
@ -47,21 +55,66 @@
`(if ,condition nil (progn ,@elses)))) `(if ,condition nil (progn ,@elses))))
; Define the dotimes iteration macro. ; Impement the cond form as nested if's. A special case is a (condition)
; As the variable has to be bound locally for elisp, this needs to go through ; subform, in which case we need to return the condition itself if it is true
; the dynamic scoping fluid system. So we can't speed these forms up by ; and thus save it in a local variable before testing it.
; implementing them directly in the compiler with just a lexical variable
; anyways. (built-in-macro cond
; For dolist, on the other hand, we have to bind the elisp variable to the (lambda (. clauses)
; list elements but keep track of the list-tails in another one. Therefore, (let iterate ((tail clauses))
; this can take advantage of real compilation because of circumventing the (if (null? tail)
; fluid-system for this variable. 'nil
(let ((cur (car tail))
(rest (iterate (cdr tail))))
(prim cond
((prim or (not (list? cur)) (null? cur))
(macro-error "invalid clause in cond" cur))
((null? (cdr cur))
(let ((var (gensym)))
`(without-void-checks (,var)
(lexical-let ((,var ,(car cur)))
(if ,var
,var
,rest)))))
(else
`(if ,(car cur)
(progn ,@(cdr cur))
,rest))))))))
; The and and or forms can also be easily defined with macros.
(built-in-macro and
(lambda (. args)
(if (null? args)
't
(let iterate ((tail args))
(if (null? (cdr tail))
(car tail)
`(if ,(car tail)
,(iterate (cdr tail))
nil))))))
(built-in-macro or
(lambda (. args)
(let iterate ((tail args))
(if (null? tail)
'nil
(let ((var (gensym)))
`(without-void-checks (,var)
(lexical-let ((,var ,(car tail)))
(if ,var
,var
,(iterate (cdr tail))))))))))
; Define the dotimes and dolist iteration macros.
(built-in-macro dotimes (built-in-macro dotimes
(lambda (args . body) (lambda (args . body)
(if (or (not (list? args)) (if (prim or (not (list? args))
(< (length args) 2) (< (length args) 2)
(> (length args) 3)) (> (length args) 3))
(macro-error "invalid dotimes arguments" args) (macro-error "invalid dotimes arguments" args)
(let ((var (car args)) (let ((var (car args))
(count (cadr args))) (count (cadr args)))
@ -75,6 +128,28 @@
(list (caddr args)) (list (caddr args))
'())))))) '()))))))
(built-in-macro dolist
(lambda (args . body)
(if (prim or (not (list? args))
(< (length args) 2)
(> (length args) 3))
(macro-error "invalid dolist arguments" args)
(let ((var (car args))
(iter-list (cadr args))
(tailvar (gensym)))
(if (not (symbol? var))
(macro-error "expected symbol as dolist variable")
`(let (,var)
(without-void-checks (,tailvar)
(lexical-let ((,tailvar ,iter-list))
(while (not (null ,tailvar))
(setq ,var (car ,tailvar))
,@body
(setq ,tailvar (cdr ,tailvar)))
,@(if (= (length args) 3)
(list (caddr args))
'())))))))))
; Pop off the first element from a list or push one to it. ; Pop off the first element from a list or push one to it.