Replacing a function in an expression

Discussion of Common Lisp
stackman
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Replacing a function in an expression

Post by stackman » Sun Nov 11, 2012 3:34 am

I am trying to understand the "code-is-data" motto in common lisp.
So I have an expression and I want to replace all occurrences of the function list by my-list, as follows.

Code: Select all

(defparameter *expr* '(let ((list (quote (1 2 3)))) ;; comment list
                         (append list (list 4 5 'list "list, 'list, LIST"))))
(do-replacement  *expr*)
=>(LET ((LIST '(1 2 3)))
  (APPEND LIST (MY-LIST 4 5 'LIST "list, 'list, LIST")))
It seems that using the subst or subst-if function is a good idea,
but how do I check if the symbol list is used as a function in the expression?

Konfusius
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: Replacing a function in an expression

Post by Konfusius » Sun Nov 11, 2012 4:57 am

Code: Select all

(defun do-replacement (expr)
  (setf expr (macroexpand expr))
  (cond ((atom expr) expr)
		((eq 'list (car expr)) (cons 'my-list (mapcar #'do-replacement (cdr expr))))
		((special-operator-p (car expr))
        (case (car expr)
         ; special forms need special handling
         (quote expr)
         (let (list* 'let
                  (mapcar
                    (lambda (bind)
                    (if (symbolp bind)
                     bind
                     (list (car bind) (do-replacement (cadr bind)))))
                  (cadr expr))
                 (mapcar #'do-replacement (cddr expr))))
         (t (error "special operator ~s not supported" (car expr)))))
      (t (mapcar #'do-replacement expr))))

(do-replacement
  '(let ((list '(list 1 2 3)))
   (append list (list 4 5 'list "list, 'list, LIST"))))
Unfortunately there is no implementation independent way to do this since every implementations has it's own special forms. But usually the number of special forms is small wich makes it feasible to write different versions for different implementations.
Last edited by Konfusius on Sun Nov 11, 2012 1:18 pm, edited 1 time in total.

Goheeca
Posts: 271
Joined: Thu May 10, 2012 12:54 pm
Contact:

Re: Replacing a function in an expression

Post by Goheeca » Sun Nov 11, 2012 5:54 am

I think in a real situation macrolet is more appropriate and if we want to implement do-replacement I'd do simply this:

Code: Select all

(defun do-replacement (expr)
  `(macrolet ((list (&rest args) `(my-list ,@args))) ,expr))
Yeah, if expr contains shadowing of list, it isn't replaced, but it's almost always the right thing.
You can also use macroexpand-dammit (it's available via quicklisp) to see what it's doing:

Code: Select all

(defun do-replacement (expr)
  (macroexpand-dammit:macroexpand-dammit
    `(macrolet ((list (&rest args) `(my-list ,@args))) ,expr)))
Konfusius your solution doesn't work for me, I get an output the same as an input.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.

Konfusius
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: Replacing a function in an expression

Post by Konfusius » Sun Nov 11, 2012 1:21 pm

Goheeca wrote:Konfusius your solution doesn't work for me, I get an output the same as an input.
I've corrected the bug. It should work, now. But your solution is better.

stackman
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Re: Replacing a function in an expression

Post by stackman » Sun Nov 11, 2012 2:02 pm

Unfortunately, the code provided by Goheeca does not work for me.
I never used the macrolet operator before, maybe someone can point to a beginner friendly
introduction to macrolet? I have used macros before, but I don't understand the macrolet section
of the hyperspec at the moment.

Code: Select all

CL-USER>  (defun do-replacement (expr)
                   `(macrolet ((list (&rest args) `(my-list ,@args))) ,expr))
(do-replacement  '(let ((list (quote (1 2 3)))) ;; comment list
                         (append list (list 4 5 'list "list, 'list, LIST"))))
=> (MACROLET ((LIST (&REST ARGS)
             `(MY-LIST ,@ARGS)))
  (LET ((LIST '(1 2 3)))
    (APPEND LIST (LIST 4 5 'LIST "list, 'list, LIST"))))

Goheeca
Posts: 271
Joined: Thu May 10, 2012 12:54 pm
Contact:

Re: Replacing a function in an expression

Post by Goheeca » Sun Nov 11, 2012 2:15 pm

As let is for lexical variables, as flet is for functions, the macrolet is for macros.
An example:

Code: Select all

(defun add (&rest args) (apply #'+ args))
(defun multiply (&rest args) (apply #'* args))
(add 2 3)
(macrolet ((add (&rest args) `(multiply ,@args))) (add 2 3))
// If I used + instead of add, it would complain about a package lock.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.

Paul
Posts: 106
Joined: Tue Jun 02, 2009 6:00 am

Re: Replacing a function in an expression

Post by Paul » Sun Nov 11, 2012 2:26 pm

Konfusius wrote:Unfortunately there is no implementation independent way to do this since every implementations has it's own special forms. But usually the number of special forms is small wich makes it feasible to write different versions for different implementations.
No. The special forms/operators are defined in the standard. Implementations are permitted to implement other macros as special operators, but must also supply macro definitions, so a code-walker doesn't have to know about them.

stackman
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Re: Replacing a function in an expression

Post by stackman » Sun Nov 11, 2012 2:32 pm

Thanks for the answer, Goheeca, still I fail to see how it addresses the question.
macrolet does expansion then evaluation, doesn't it?
I want to transform the source code without evaluating it, in order to modify programmatically source files.

stackman
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Re: Replacing a function in an expression

Post by stackman » Sun Nov 11, 2012 2:39 pm

@ Paul
Are there implementation independant common lisp code walkers?
A quick google search suggests that there are a few edge cases which are not so easy to handle.

I am using sbcl, so maybe there is a way to leverage some sbcl compatible code walker
to deal with code substitution, without having to roll my own?

Goheeca
Posts: 271
Joined: Thu May 10, 2012 12:54 pm
Contact:

Re: Replacing a function in an expression

Post by Goheeca » Sun Nov 11, 2012 3:47 pm

Check the version with macroexpand-dammit out, but it also expands into the implementation-dependent code because of compiler macros (for example append with two args into sb-impl::append2 under SBCL). Certainly you can be inspired by the source code of macroexpand-dammit for your version without compiler-macro expanding - I won't help you with this.
Moreover, I'd ask is it an ad hoc issue or you just need it?
In the first case I'd still let the wrapping macrolet in a source code.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.

Post Reply