Reputation: 31
I'm wondering how the case macro works, but just expanding it is not enough. How does it generate the cond statement without knowing how many arguments there are? Does it use a loop or something? And if so then why does it not show up when i run macroexpand.
I need to write something that works in a similar way, that's why I ask.
Upvotes: 1
Views: 346
Reputation: 60004
Yes you would need to use iteration - one of loop
, do
, mapcar
&c (or recursion).
Take a look at, e.g., CLISP's implementation of case
:
(defun case-expand (whole-form form-name test keyform clauses)
(let ((var (gensym (string-concat (symbol-name form-name) "-KEY-"))))
`(let ((,var ,keyform))
(cond
,@(maplist
#'(lambda (remaining-clauses)
(let ((clause (first remaining-clauses))
(remaining-clauses (rest remaining-clauses)))
(unless (consp clause)
(error-of-type 'source-program-error
:form whole-form
:detail clause
(TEXT "~S: missing key list")
form-name))
(let ((keys (first clause)))
`(,(cond ((or (eq keys 'T) (eq keys 'OTHERWISE))
(if remaining-clauses
(error-of-type 'source-program-error
:form whole-form
:detail clause
(TEXT "~S: the ~S clause must be the last one")
form-name keys)
't))
((listp keys)
`(or ,@(mapcar #'(lambda (key)
`(,test ,var ',key))
keys)))
(t `(,test ,var ',keys)))
,@(rest clause)))))
clauses)))))
(defmacro case (&whole whole-form
keyform &body clauses)
(case-expand whole-form 'case 'eql keyform clauses))
Upvotes: 1