Replacing a function in an expression

Discussion of Common Lisp

Replacing a function in an expression

Postby 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?
stackman
 
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Re: Replacing a function in an expression

Postby 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.
Konfusius
 
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: Replacing a function in an expression

Postby 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).
User avatar
Goheeca
 
Posts: 218
Joined: Thu May 10, 2012 12:54 pm

Re: Replacing a function in an expression

Postby 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.
Konfusius
 
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: Replacing a function in an expression

Postby 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"))))
stackman
 
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Re: Replacing a function in an expression

Postby 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).
User avatar
Goheeca
 
Posts: 218
Joined: Thu May 10, 2012 12:54 pm

Re: Replacing a function in an expression

Postby 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.
Paul
 
Posts: 106
Joined: Tue Jun 02, 2009 6:00 am

Re: Replacing a function in an expression

Postby 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

Postby 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?
stackman
 
Posts: 28
Joined: Sat Oct 06, 2012 5:44 am

Re: Replacing a function in an expression

Postby 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).
User avatar
Goheeca
 
Posts: 218
Joined: Thu May 10, 2012 12:54 pm

Next

Return to Common Lisp

Who is online

Users browsing this forum: Google [Bot] and 3 guests