public inbox for guile-emacs@sourceware.org
 help / color / mirror / Atom feed
From: Keisuke Nishida <kxn30@po.cwru.edu>
To: guile-emacs@sourceware.cygnus.com
Cc: guile@sourceware.cygnus.com
Subject: Re: Emacs Scheme interface
Date: Fri, 31 Mar 2000 17:48:00 -0000	[thread overview]
Message-ID: <m3k8iibnh5.fsf@kei.cwru.edu> (raw)
In-Reply-To: <m3ln33ege5.fsf@kei.cwru.edu>

Hello,

This is an idea of the new low level interface between Lisp and Scheme
that uses GOOPS for Guile Emacs.

Construction Process
--------------------

1. When one calls lisp-eval in Scheme, that calls Feval (the Emacs's
   eval function) internally.

2. Feval returns a Lisp object.  We have to create a corresponding
   Scheme object.  If the object is directly convertible to a Scheme
   object, we will do so.  However, since we want to share storage
   between Lisp and Scheme, we are going to create a kind of reference
   to the Lisp object.  We call it an instance of Emacs Object that is
   a GOOPS class.

  - Class: <emacs-object>

    An abstract class to hold Lisp objects.  Subclasses of this
    class are actually used.

  - Class: <emacs-pair>
  - Class: <emacs-string>
  - Class: <emacs-vector>
  - Class: <emacs-buffer>
  ...

3. We want to return the same Emacs object for the same Lisp object.
   We need a table to manage this relationship.  Let's think of it
   as a pair of vectors:

     Lisp object vector:  [obj0][obj1][obj2][obj3][...]
   Scheme object vector:  [obj0][obj1][obj2][obj3][...]

   The elements of the Lisp vector are Lisp objects.  The elements of
   the Scheme vector are corresponding Emacs objects mentioned above.
   We can traverse the Lisp vector to find the index of a Lisp object
   and thus the corresponding Emacs object.  An Emacs object should
   have a slot that keeps the index so that we can efficiently find
   the corresponding Lisp object.  (Or it could have a slot to hold
   a Lisp object directly, but we need the index slot anyway as
   described below.)

   This vector implementation is not very efficient, but I'm going to
   write this as the initial implementation because of its easiness.
   The Lisp vector is also used to protect those Lisp objects from GC.

4. We have to create a new Emacs object if it has not been done.  We
   put a Lisp object in the vector, get the index, and make a Emacs
   object with the index like this (but in the C level):

     (make <emacs-object> #:index index)

   The class is practically determined by the type of the Lisp object.
   We put this object in the Scheme vector for later reference.

5. The Emacs object found/created is returned as the value of lisp-eval.

Modification Process
--------------------

Since Emacs uses the same object type for several different concepts
(e.g., integers for characters, lists for sparse keymaps), we want to
distinguish them.  This could be done like this:

  (define-class <emacs-keymap> (<emacs-object>) ...)

  (define (current-local-map)
    (make <emacs-keymap> #:value (lisp-eval '(current-local-map))))

This is not sufficient since it does not always return the same value.
We need to change the class some way like this:

  (define (current-local-map)
    (let ((keymap (lisp-eval '(current-local-map))))
      (%change-emacs-object-class! keymap <emacs-keymap>)
      keymap))

This may be a little bit tricky, so I am not sure if we should do this.

Application Process
-------------------

Once lisp-eval returns a GOOPS object, we can import a Lisp function
and use it like this:

  (define (buffer-string)
    (lisp-eval '(buffer-string)))

  (define-method string-ref ((string <emacs-string>) (n <integer>))
    (lisp-eval (list 'aref string n)))

  (string-ref (buffer-string) 0)  --> 123

If a Emacs object is used as an argument of a Lisp function, it is
automatically converted to the corresponding Lisp object due to the
table defined above, so we don't need to care about that.  We can use
both <string> and <emacs-string> as an argument of the function insert:

  (define insert (string)
    (lisp-eval (list 'insert string)))

  (insert "hello")              ;; OK
  (insert (buffer-string))      ;; OK

If we need to operate an Emacs object by using Scheme procedures,
an explicit conversion is necessary.  It can be done like this:

  (define-method eval-string ((string <emacs-string>))
    (eval-string (%emacs-object->scheme string)))

  (eval-string (buffer-string))

At this level, most operations of lisp variables/functions except
dynamic references can be done transparently, and the code can be
used with the Guile-based Emacs in the future.  (See below for how
to handle dynamic bindings in Scheme.)

Destruction Process
-------------------

When an Emacs object is garbage-collected, we also have to remove
the corresponding Lisp object from the table.  We can use the index
slot of the Emacs object to find the location.

In the vector implementation, we can replace the Lisp object by nil.
That field can be used later.  If there are too many nil fields, we
should squeeze the elements.

Dynamic Bidings
---------------

Dynamic bidings, including references to buffer/frame-local variables,
are done this way:

  (define (lisp-variable-ref var)
    (lisp-eval var))

  (define (lisp-variable-set! var val)
    (lisp-eval `(setq ,var ',val)))

  (define buffer-file-name
    (make-procedure-with-setter
     (lambda () (lisp-variable-ref 'buffer-file-name))
     (lambda (value) (lisp-variable-set! 'buffer-file-name value))))

  (set! (buffer-file-name) "hello")
  (insert (buffer-file-name))

Note that we can't write without parentheses like (insert buffer-file-name),
which we can do with the current implementation.  We should explicitly
indicate what we are doing by using parentheses (and it's less tricky).

I think these interfaces can be used in the future and make it easier
to switch to Guile-based Emacs whenever it is appropriate.

-- Kei

  parent reply	other threads:[~2000-03-31 17:48 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2000-03-27 22:51 Keisuke Nishida
2000-03-28  4:01 ` Kalle Olavi Niemitalo
2000-03-28 10:27   ` Keisuke Nishida
2000-03-28 11:37     ` Ken Raeburn
2000-03-28 23:06     ` Kalle Olavi Niemitalo
2000-03-29 16:57       ` Keisuke Nishida
2000-03-28 11:43 ` Kalle Olavi Niemitalo
2000-03-28 12:23   ` Keisuke Nishida
2000-03-29  2:33     ` Kalle Olavi Niemitalo
2000-03-29 17:09       ` Keisuke Nishida
2000-04-30 12:08         ` Kalle Olavi Niemitalo
2000-03-31 17:48 ` Keisuke Nishida [this message]
2000-03-31 22:42   ` Klaus Schilling
2000-04-01  9:02     ` Keisuke Nishida

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m3k8iibnh5.fsf@kei.cwru.edu \
    --to=kxn30@po.cwru.edu \
    --cc=guile-emacs@sourceware.cygnus.com \
    --cc=guile@sourceware.cygnus.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).