From mboxrd@z Thu Jan 1 00:00:00 1970 From: Keisuke Nishida 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 Message-id: References: X-SW-Source: 2000-q1/msg00078.html 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: An abstract class to hold Lisp objects. Subclasses of this class are actually used. - Class: - Class: - Class: - Class: ... 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 #: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 () ...) (define (current-local-map) (make #: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 ) 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 ) (n )) (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 and 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 )) (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