-*- outline -*- This directory holds the Scheme side of a translator for Emacs Lisp. * Usage To load up the base Elisp environment: (use-modules (lang elisp base)) Then you can switch into this module (define-module (lang elisp base)) and start typing away in Elisp, or evaluate an individual Elisp expression from Scheme: (eval EXP (resolve-module '(lang elisp base))) A more convenient, higher-level interface is provided by (lang elisp interface): (use-modules (lang elisp interface)) With this interface, you can evaluate an Elisp expression (eval-elisp EXP) load an Elisp file with no effect on the Scheme world (load-elisp-file "/home/neil/Guile/cvs/guile-core/lang/elisp/example.el") load an Elisp file, automatically importing top level definitions into Scheme (use-elisp-file "/home/neil/Guile/cvs/guile-core/lang/elisp/example.el") export Scheme objects to Elisp (export-to-elisp + - * my-func 'my-var) and try to bootstrap a complete Emacs environment: (load-emacs) * Status Please see the STATUS file for the full position. ** Trying to load a complete Emacs environment. To try this, type `(use-modules (lang elisp interface))' and then `(load-emacs)'. The following output shows how far I get when I try this. guile> (use-modules (lang elisp interface)) guile> (load-emacs) Calling loadup.el to clothe the bare Emacs... Loading /usr/share/emacs/20.7/lisp/loadup.el... Using load-path ("/usr/share/emacs/20.7/lisp/" "/usr/share/emacs/20.7/lisp/emacs-lisp/") Loading /usr/share/emacs/20.7/lisp/byte-run.el... Loading /usr/share/emacs/20.7/lisp/byte-run.el...done Loading /usr/share/emacs/20.7/lisp/subr.el... Loading /usr/share/emacs/20.7/lisp/subr.el...done Loading /usr/share/emacs/20.7/lisp/version.el... Loading /usr/share/emacs/20.7/lisp/version.el...done Loading /usr/share/emacs/20.7/lisp/map-ynp.el... Loading /usr/share/emacs/20.7/lisp/map-ynp.el...done Loading /usr/share/emacs/20.7/lisp/widget.el... Loading /usr/share/emacs/20.7/lisp/emacs-lisp/cl.el... Loading /usr/share/emacs/20.7/lisp/emacs-lisp/cl.el...done Loading /usr/share/emacs/20.7/lisp/widget.el...done Loading /usr/share/emacs/20.7/lisp/custom.el... Loading /usr/share/emacs/20.7/lisp/custom.el...done Loading /usr/share/emacs/20.7/lisp/cus-start.el... Note, built-in variable `abbrev-all-caps' not bound ... [many other variable not bound messages] ... Loading /usr/share/emacs/20.7/lisp/cus-start.el...done Loading /usr/share/emacs/20.7/lisp/international/mule.el... : In procedure make-char-table in expression (@fop make-char-table (# #)): : Symbol's function definition is void ABORT: (misc-error) Type "(backtrace)" to get more information or "(debug)" to enter the debugger. guile> That's 3279 lines ("wc -l") of Elisp code already, which isn't bad! I think that progress beyond this point basically means implementing multilingual and multibyte strings properly for Guile. Which is a _lot_ of work and requires IMO a very clear plan for Guile's role with respect to Emacs. * Design When thinking about how to implement an Elisp translator for Guile, it is important to realize that the great power of Emacs does not arise from Elisp (seen as a language in syntactic terms) alone, but from the combination of this language with the collection of primitives provided by the Emacs C source code. Therefore, to be of practical use, an Elisp translator needs to be more than just a transformer that translates sexps to Scheme expressions. The finished translator should consist of several parts... ** Syntax transformation Although syntax transformation isn't all we need, we do still need it! This part is implemented by the (lang elisp transform) module; it is close to complete and seems to work pretty reliably. Note that transformed expressions use the `@fop' and `@bind' macros provided by... ** C support for transformed expressions For performance and historical reasons (and perhaps necessity - I haven't thought about it enough yet), some of the transformation support is written in C. *** @fop The `@fop' macro is used to dispatch Elisp applications. Its first argument is a symbol, and this symbol's function slot is examined to find a procedure or macro to apply to the remaining arguments. `@fop' also handles aliasing (`defalias'): in this case the function slot contains another symbol. Once `@fop' has found the appropriate procedure or macro to apply, it returns an application expression in which that procedure or macro replaces the `@fop' and the original symbol. Hence no Elisp-specific evaluator support is required to perform the application. *** @bind Currently, Elisp variables are the same as Scheme variables, so variable references are effectively untransformed. The `@bind' macro does Elisp-style dynamic variable binding. Basically, it locates the named top level variables, `set!'s them to new values, evaluates its body, and then uses `set!' again to restore the original values. Because of the body evaluation, `@bind' requires evaluator support. In fact, the `@bind' macro code does little more than replace itself with the memoized SCM_IM_BIND. Most of the work is done by the evaluator when it hits SCM_IM_BIND. One theoretical problem with `@bind' is that any local Scheme variable in the same scope and with the same name as an Elisp variable will shadow the Elisp variable. But in practice it's difficult to set up such a situation; an exception is the translator code itself, so there we mangle the relevant Scheme variable names a bit to avoid the problem. Other possible problems with this approach are that it might not be possible to implement buffer local variables properly, and that `@bind' might become too inefficient when we implement full support for undefining Scheme variables. So we might in future have to transform Elisp variable references after all. *** Truth value stuff Following extensive discussions on the Guile mailing list between September 2001 and January 2002, we decided to go with Jim Blandy's proposal. See devel/translation/lisp-and-scheme.text for details. - The Elisp nil value is a new immediate SCM_MAKIFLAG, eq?-distinct from both #f and '() (and of course any other Scheme value). It can be accessed via the (guile) binding `%nil', and prints as `#nil'. - All Elisp primitives treat #nil, #f and '() as identical. - Scheme truth-testing primitives have been modified so that they treat #nil the same as #f. - Scheme list-manipulating primitives have been modified so that they treat #nil the same as '(). - The Elisp t value is the same as #t. ** Emacs editing primitives Buffers, keymaps, text properties, windows, frames etc. etc. Basically, everything that is implemented as a primitive in the Emacs C code needs to be implemented either in Scheme or in C for Guile. The Scheme files in the primitives subdirectory implement some of these primitives in Scheme. Not because that is the right decision, but because this is a proof of concept and it's quicker to write badly performing code in Scheme. Ultimately, most of these primitive definitions should really come from the Emacs C code itself, translated or preprocessed in a way that makes it compile with Guile. I think this is pretty close to the work that Ken Raeburn has been doing on the Emacs codebase. ** Reading and printing support Elisp is close enough to Scheme that it's convenient to coopt the existing Guile reader rather than to write a new one from scratch, but there are a few syntactic differences that will require changes in reading and printing. None of the following changes has yet been implemented. - Character syntax is `?a' rather than `#\a'. (Not done. More precisely, `?a' in Elisp isn't character syntax but an alternative integer syntax. Note that we could support most of the `?a' syntax simply by doing (define ?a (char->integer #\a) (define ?b (char->integer #\b) and so on.) - Vector syntax is `[1 2 3]' rather than `#(1 2 3)'. - When in an Elisp environment, #nil and #t should print as `nil' and `t'. ** The Elisp evaluation module (lang elisp base) Fundamentally, Guile's module system can't be used to package Elisp code in the same way it is used for Scheme code, because Elisp function definitions are stored as symbol properties (in the symbol's "function slot") and so are global. On the other hand, it is useful (necessary?) to associate some particular module with Elisp evaluation because - Elisp variables are currently implemented as Scheme variables and so need to live in some module - a syntax transformer is a property of a module. Therefore we have the (lang elisp base) module, which acts as the repository for all Elisp variables and the site of all Elisp evaluation. The initial environment provided by this module is intended to be a non-Emacs-dependent subset of Elisp. To get the idea, imagine someone who wants to write an extension function for, say Gnucash, and simply prefers to write in Elisp rather than in Scheme. He/she therefore doesn't buffers, keymaps and so on, just the basic language syntax and core data functions like +, *, concat, length etc., plus specific functions made available by Gnucash. (lang elisp base) achieves this by - importing Scheme definitions for some Emacs primitives from the files in the primitives subdirectory - then switching into Elisp syntax. After this point, `(eval XXX (resolve-module '(lang elisp base)))' will evaluate XXX as an Elisp expression in the (lang elisp base) module. (`eval-elisp' in (lang elisp interface) is a more convenient wrapper for this.) ** Full Emacs environment The difference between the initial (lang elisp base) environment and a fully loaded Emacs equivalent is - more primitives: buffers, char-tables and many others - the bootstrap Elisp code that an undumped Emacs loads during installation by calling `(load "loadup.el")'. We don't have all the missing primitives, but we can already get through some of loadup.el. The Elisp function `load-emacs' (defined in (lang elisp base) initiates the loading of loadup.el; (lang elisp interface) exports `load-emacs' to Scheme. `load-emacs' loads so much Elisp code that it's an excellent way to test the translator. In current practice, it runs for a while and then fails when it gets to an undefined primitive or a bug in the translator. Eventually, it should go all the way. (And then we can worry about adding unexec support to Guile!) For the output that currently results from calling `(load-emacs)', see above in the Status section. * Resources ** Ken Raeburn's Guile Emacs page http://www.mit.edu/~raeburn/guilemacs/ ** Keisuke Nishida's Gemacs project http://gemacs.sourceforge.net ** Jim Blandy's nil/#f/() notes http://sanpietro.red-bean.com/guile/guile/old/3114.html Also now stored as guile-core/devel/translation/lisp-and-scheme.text in Guile CVS. ** Mikael Djurfeldt's notes on translation See file guile-core/devel/translation/langtools.text in Guile CVS.