Question

Common Lisp macros typically use included-prefix notation: (operator stuff...)

However, the special quote macro ' uses concatenated-prefix notation: operator stuff , or alternatively operator(stuff).

I would like to create a custom macro in SBCL Common Lisp, let's call it !, that uses concatenation-prefix syntax to operate on a following list (or even atom), in a similar manner to ' . So I could invoke it anywhere, e.g. (setq foo !(bar) ), without it being infixed in parentheses itself.

How can this be done? What would the defmacro syntax look like? Thanks.

Was it helpful?

Solution

I see 2 ways to do it.

The first, simple variant is suitable, if you need it only for a single or very limited number of cases and you're satisfied with using just a single symbol as a prefix (like ! in the example case). You can create a read-macro, more specifically set a macro-character to substitute such operator for, say, addition:

(set-macro-character #\! (lambda (stream char)
                           (declare (ignore char))
                           (cons '+ (let ((next (read stream t nil t)))
                                 (if (consp next) next
                                     (list next)))))
CL-USER> !1
1
CL-USER> !(1 2)
3

The other, more complex and more flexible way is to define a macro, inside which apply a custom transformation, that will turn concatenated-prefix code into included-prefix. This trick uses the fact, that ordinary Lisp reader will read the in the same way both foo(bar) and foo (bar), i.e. it will split them into 2 elements.

The simple version of such macro may look like this:

(defmacro with-prefix-syntax (&body body)
  `(progn ,@(loop :for tail :on body :while body
                  :collect (if (and (not (atom (second tail)))
                                    (fboundp (first tail)))
                               (prog1 (cons (first tail) (second tail)
                                 (setf tail (rest tail)))
                               (first tail)))))

It will transform only top-level forms:

CL-USER> (macroexpand-1 '(with-prefix-syntax
                           print(1)))
(PROGN (PRINT 1))
CL-USER> (macroexpand-1 '(with-prefix-syntax
                           1))
(PROGN 1)
CL-USER> (macroexpand-1 '(with-prefix-syntax
                           print(1)
                           2))
(PROGN (PRINT 1) 2)

But not work in the lower levels:

CL-USER> (macroexpand-1 '(with-prefix-syntax
                           print(1)
                           (+ print(2))))
(PROGN (PRINT 1) (+ PRINT (2)))

Though it's rather easy to make it recursively transform all the layers (which is left as an exercise to the reader :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top