Coming back to your original PLUS question (because the examples explain better what I mean), here is a PLUS macro that can compute normal numbers as well as string numbers from "zero" to "nine":
Code: Select all
(defmacro plus (&rest args)
`(apply #'+ ',(mapcar #'(lambda (x)
(cond ((numberp x) x)
((stringp x)
(cond ((string-equal x "zero") 0)
((string-equal x "one") 1)
((string-equal x "two") 2)
((string-equal x "three") 3)
((string-equal x "four") 4)
((string-equal x "five") 5)
((string-equal x "six") 6)
((string-equal x "seven") 7)
((string-equal x "eight") 8)
((string-equal x "nine") 9)
(t (error "unknown string ~s" x))))
(t (error "illegal argument ~s" x))))
args)))
Here is how it works:
Code: Select all
CL-USER> (macroexpand-1 '(plus "one" "two" "three"))
(APPLY #'+ '(1 2 3))
T
CL-USER> (plus "one" "two" "three")
6
The argument dispatcher can be made a separate function:
Code: Select all
(defun arg-dispatcher (x)
(cond ((numberp x) x)
((stringp x)
(cond ((string-equal x "zero") 0)
((string-equal x "one") 1)
((string-equal x "two") 2)
((string-equal x "three") 3)
((string-equal x "four") 4)
((string-equal x "five") 5)
((string-equal x "six") 6)
((string-equal x "seven") 7)
((string-equal x "eight") 8)
((string-equal x "nine") 9)
(t (error "unknown string ~s" x))))
(t (error "illegal argument ~s" x))))
The arg-dispatcher function is called from within the macro definition:
Code: Select all
(defmacro plus (&rest args)
`(apply #'+ ',(mapcar #'arg-dispatcher args)))
CL-USER> (macroexpand-1 '(plus "one" "two" "three"))
(APPLY #'+ (1 2 3))
T
This can be even simplified to:
Code: Select all
(defmacro plus (&rest args)
`(+ ,@(mapcar #'arg-dispatcher args)))
CL-USER> (macroexpand-1 '(plus "one" "two" "three"))
(+ 1 2 3)
T
CL-USER> (plus "one" "two" "three")
6
Using an argument dispatcher also works with special operators and macros.
Special note: In my examples above above it's important that the dispatcher works independent from any local state from the environment where the macro is expanded (usually lexical values from LET variables surrounding the macro call). If the local environment is needed for correct dipatch then things start to get more hairy like in Goheeca's GET-ENV example.
The general pattern is:
Code: Select all
(defmacro <macro-name> (&rest args)
(<function-special-or-macro> ,@(mapcar #'<lambda-or-dispatch-function> args)))
Is this what you are looking for?
- edgar
P.S.: Paul Graham's
On Lisp contains lots of macro tricks.