help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Using setq to obtain a symbol from a list, so that I can assign a f


From: Pascal Bourguignon
Subject: Re: Using setq to obtain a symbol from a list, so that I can assign a function to it
Date: Tue, 22 Apr 2008 21:59:58 +0200
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/22.1 (gnu/linux)

address@hidden writes:

> Hello,
>
> I would really appreciate some help on this. I am trying to do just
> about everything with Emacs (and Emacs Lisp) these days. One of the
> things I need to do in my job is to create XML files. I thought I
> would use Emacs rather than some other editor/tool.
>
> So, I am trying to write a tool that will enable me to write specific
> XML files by prompting me to enter values. Here is how I am trying to
> do it.
>
> First, I set a variable that will provide a "grammar" for my file that
> Emacs will help me write. An example is the following : of course, my
> actual problem is not about generating English sentences; this is just
> for illustration.
>
> (setq grammar '((sentence  <- subject predicate)
>                 (subject   <- article noun)
>                 (predicate <- verb)))
>
> I expect to generate functions that look like this:
>
> (defun sentence()
>   (........some stuff .........)
>   (subject)
>   (predicate)
>   (........some stuff again...))
>
> What I want the system to do is to generate the function
> automatically. So, I should have functions for sentence, subject and
> predicate. I will of course hand-code the functions for article, noun
> and verb.
>
> I don't want to write the above defun; I want to generate it by using
> (setq sentence #'(lambda() ( (subject) (predicate)))). Of course,
> instead of "sentence". I want to use (car x) or something like that by
> reading the variable "grammar".
>
> Here is the problem: when I do this:
>
> (setq (car (expression that generates sentence) #'(lambda() (print
> "hello")))
>
> I get an error.
>
> But if I do a (setq x (car ... ) to get the sentence symbol and THEN
> assign it, everything works.
>
> Is there something fundamental I am missing?

You're near.

First, remember that emacs lisp is a lisp-2.  That means that a symbol
can have both a function and a value.

defun  sets the function of the symbol.
defvar (setf and setq) set the value of the symbol.

(This is in opposition to scheme which is a lisp-1 and where you only
need to assign a procedure value to a variable to define a function).


So what do you want to generate?
(defun sentence ...) or (setq  sentence (lambda ....)) ?


Also, something you might consider is that there is no package in
emacs lisp, so whether you use the function slot or the value slot of
these symbol, it is global.  Imagine you want to edit an XML
describing some graphic with lines and arrows at the beginning or the
end of the lines.  You could have have this kind of grammar:

(define-grammar graphic-xml
  (graphic           -> lines) 
  (lines             -> )
  (lines             -> line lines)
  (line              -> beginning-of-line line-attributes end-of-lines)
  (beginning-of-line -> x y decoration)
  (end-of-line       -> x y decoration))

Oops! You just redefined the emacs lisp functions beginning-of-line
and end-of-line, and about 30% of the keypresses will now break...

One solution would be to build composed symbol names.  For example,
the global functions and variables defined by ERC, the Emacs iRc
Client all start with erc-.  Another solution is to keep your
generated functions in a hash-table indexed by the symbols.  In this
case, the hash-table defines in a way another namespace. 




Ok, so now let's address your main question.  What you have is
a list containing a grammar production like: 

         ( <name>   ->   <item1> ... <itemN> )

and you want to get a _list_ like:

         (defun <name> ()
             (<item1>)
             ...
             (<itemN>))

Well, there's really no difficulty.  I'm sure you could easily do it.
if defun gives you difficulties, try to generate this list instead:

         (donald <name> mickey  (<item1>) ... (<itemN>))





Here it is:

    (defun generate-production (prod)
       (destructuring-bind (name separator &rest items) prod
          (list* 'defun name '()
                 (mapcar (lambda (item) (list item)) items))))


    (generate-production '(sentence  <- subject predicate))
    --> (defun sentence nil (subject) (predicate))



On the other hand, if you want to put these function into a
hash-table, you could write:

    (defun generate-rhs (items)
       (list* 'lambda '()
               (mapcar (lambda (item) (list item)) items)))

    (defun store-production (grammar production)
       (destructuring-bind (name separator &rest items) production
          (setf (gethash name grammar) (byte-compile (generate-rhs items)))))

    (setq grammar (make-hash-table))
    (store-production grammar '(sentence <- subject predicate))


We can call the function associated with sentence with:

    (funcall (gethash 'sentence grammar))

Note that we don't generate a setf, we just generate the (lambda ...)
compile it, and put the resulting compiled function into the
hash-table.

As for:
> (setq (car (expression that generates sentence) #'(lambda() (print
> "hello")))
yes, you can use set in this situation:

  (destructuring-bind (name separator &rest items) production
     (set name (byte-compile `(lambda () ,@(mapcar 'list items)))))

or, equivalently setf symbol-value:

  (destructuring-bind (name separator &rest items) production
     (setf (symbol-value name) (byte-compile `(lambda () ,@(mapcar 'list 
items)))))

but it's probably better to but these function in a hash-table than in
the value slots of the symbols.



Instead of writting deep expressions such as:

   (list 'lambda '() 
         (append '(save-excursion)
                  (list (list 'insert '"<" (string name) '">"))
                  forms
                  (list (list 'insert '"</" (string name) '">"))))

you can use backquote and comma to write it more easily:

   `(lambda () 
       (save-excursion
          (insert "<"  ,(string name) ">")
          ,@forms
          (insert "</" ,(string name) ">")))


so for example, you could write:

    (defun make-name (name)
      (intern (format "xml-insert/%s" name)))

    (defun generate-production (prod)
       (destructuring-bind (name separator &rest items) prod
          `(defun ,(make-name name) ()
              (insert ,(format "<%s>" name))
              ,@(mapcar (lambda (item) `(,(make-name item))) items)
              (insert ,(format "</%s>" name)))))

(generate-production '(sentence  <- subject predicate))
--> (defun xml-insert/sentence nil
       (insert "<sentence>")
       (xml-insert/subject)
       (xml-insert/predicate)
       (insert "</sentence>"))





Finally, emacs is not modal.  It would not be very emacs-like to do
M-x xml-insert/document RET and to be prompted for half a hour to
insert a whole document.  Instead, you could do something like
customize-variable.   Try for example M-x customize-variable RET
lisp-source-modes RET Most of the buffer is read-only, and there are
left some area you can edit.  There are some buttons, to insert or
remove repeatitive areas.  You could have a look at the sources of
customize-variable to see how it's done and implement your structural
editor like this.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

THIS IS A 100% MATTER PRODUCT: In the unlikely event that this
merchandise should contact antimatter in any form, a catastrophic
explosion will result.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]