Welcome to Expanders!! :D
This project lets you define expanders. An expander is like a namespace for forms that can be expanded by a macro.
It easier to understand with an example. Suppose we have the following form (op (+ 3 4) b). This form will be used in two different macros named plus-macro and minus-macro. Each macro receives a form. That form could start with op. In that case, op is substituted by + or - respectively.
Let's define 2 different expanders:
;; We assume (use-package #:expanders)
(defexpander plus-expander)
(defexpander minus-expander);; Returns
MINUS-EXPANDERNow we have two different expanders. The function exp:expanderp can tell us if a symbol denotes an expander:
(expanderp 'plus-expander);; Returns
T(expanderp 'hey);; Returns
NIL(expanderp 'minus-expander);; Returns
TNow it is time to define the expansion op for each expander using exp:defexpansion:
(defexpansion plus-expander op (a b)
"OP to + expansion"
`(+ ,a ,b))
(defexpansion minus-expander op (a b)
"OP to - expansion"
`(- ,a ,b));; Returns
OPWe can check if a symbol is an expansion for a given expander using exp:expansionp:
(expansionp 'plus-expander 'op);; Returns
T(expansionp 'plus-expander 'hey);; Returns
NIL(expansionp 'minus-expander 'op);; Returns
TAlso, we can retrieve or set the docstring using documentation:
(documentation 'op 'plus-expander);; Returns
"OP to + expansion"(let ((old-docstring (documentation 'op 'minus-expander)))
(setf (documentation 'op 'minus-expander) "Another docstring")
(let ((new-docstring (documentation 'op 'minus-expander)))
(format t "Old: ~s~%New: ~s" old-docstring new-docstring)));; Output
Old: "OP to - expansion"
New: "Another docstring"
;; Returns
NILWe can expand an expansion using exp:expand:
(expand 'plus-expander 'op 3 '(+ 5 6));; Returns
(+ 3 (+ 5 6))(expand 'minus-expander 'op 3 '(+ 5 6));; Returns
(- 3 (+ 5 6))But usually is more convenient to use the exp:expand* function:
(expand* 'plus-expander 'op '(3 (+ 5 6)));; Returns
(+ 3 (+ 5 6))or:
(expand* 'plus-expander '(op 3 (+ 5 6)));; Returns
(+ 3 (+ 5 6))Finally, let's define the macros plus-macro and minus-macro:
(defmacro plus-macro (form)
(if (and (consp form)
(expansionp 'plus-expander (car form)))
(expand* 'plus-expander form)
form));; Returns
PLUS-MACRO(defmacro minus-macro (form)
(if (and (consp form)
(expansionp 'minus-expander (car form)))
(expand* 'minus-expander form)
form));; Returns
MINUS-MACROIf we use the form (op 5 4) we will see that each macro will expand to (+ 5 4) or (- 5 4) respectively.
(plus-macro (op 5 4));; Returns
9(minus-macro (op 5 4));; Returns
1- It is common: I have noticed that having expanders is a relatively common pattern in macros. The best example is
setfand itssetf-expanders. Another project using expanders is CFFI and its type parsers. In my own projects I ended up using the same techniques (Clith). - Duality of syntax: We can increase the duality of syntax using expanders. The best example is
setf. Thanks tosetfwe don't need names for setters because they come for free from getters.
Define an expander represented by the symbol SYM.
If used at top level the expander will be defined at compile time.
Define an expansion for the expander EXPANDER. If used at top level the expansion will be defined at
compile time. NAME must be a symbol denoting the new expansion. ARGS is a destructuring lambda list.
The &whole argument can be supplied to bind a list with all the arguments.
DEFEXPANSION must return the desired expansion for NAME and EXPANDER.
Expand an EXPANSION from EXPANDER.
Expand an expansion from EXPANDER. The first argument from ARGS must be a valid expansion.
The last argument can be a symbol denoting the expansion (no arguments),
or a list with the last arguments to use in the expansion.
Examples:
(expand* 'my-expander 'my-expansion) ; No arguments
(expand* 'my-expander (list 'my-expansion arg1 arg2 ...))
(expand* 'my-expander 'my-expansion arg1 arg2 (list arg3 arg4 ...))
Check if a symbol denotes an expander.
Check if EXPANSION is a valid expansion for EXPANDER.