Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mschuldt/luamacs

E-Lisp and Lua -- merged!
https://github.com/mschuldt/luamacs

Last synced: 1 day ago
JSON representation

E-Lisp and Lua -- merged!

Awesome Lists containing this project

README

        

Luamacs -- Emacs with Lua

* Overview
Luamacs is the result of merging the Emacs-lisp and Lua interpreters.
Each of the interpreters can lookup values and call functions
from the other one.
They have each been extended with a new data type that allows them to
manipulate references to values in the others environment.

From Emacs, Lua values are referenced using the pseudo object 'lua'. For example,
the variable 'x' is referenced with 'lua.x'. From Lua, lisp variable values are
referenced using the table 'el'. Because E-lisp variables and functions
exist in separate namespaces, a separate table 'elf' is used to reference
lisp functions from Lua. Additional lisp primitives are provided for
working with lua tables directly. When lisp lists, vectors, or hashtables are
referenced from lua they are wrapped in a Lua table that provides
methods for manipulating the lisp values in a natural lua way.

* Usage
Evaluate a string as Lua code:
#+Begin_SRC emacs-lisp
(lua-eval "")
#+END_SRC
Evaluate a buffer as Lua code:
#+Begin_SRC text
M-x lua-eval-buffer
#+END_SRC
* Examples
** Basic
Evaluate lua code from lisp:
#+Begin_SRC emacs-lisp
;;create a lua function that prints a tables field:
(lua-eval "function ff(x)
return x.val
end")
;;define a lua table
(lua-eval "tbl = {val = 'lua string'}")
#+END_SRC
Now store the lua table in a lisp variable
#+Begin_SRC emacs-lisp
(setq z lua.tbl)
==> #
#+END_SRC
Lisp objects can also be stored in lua:
#+Begin_SRC emacs-lisp
(setq this_buff (current-buffer))
(lua-eval "b = el.this_buff")
(buffer-name lua.b)
==> "*scratch*"
lua.b
==> #
#+END_SRC
Call the Lua function from Emacs:
#+Begin_SRC emacs-lisp
(lua.ff z) ;; or (funcall lua.ff z)
==> "lua string"
#+END_SRC
Or refer to the table stored in Emacs:
#+Begin_SRC emacs-lisp
(lua-eval "v = ff(el.z)")
#+END_SRC
Then get the result from Emacs:
#+Begin_SRC emacs-lisp
(concat lua.v " + emacs string")
==> "lua string + emacs string"
#+END_SRC
Calling lisp functions from lua:
#+Begin_SRC emacs-lisp
(lua-eval "x = elf.concat(elf['buffer-name'](), '!!!!!')")
lua.x
==> "*scratch*!!!!!"
#+END_SRC
** Wrapped lisp values in lua
list lists, vectors, and hashtables are wrapped in Lua tables when
they are referenced from Lua, their metatables provide the methods
__index, __newindex, __len, and __pairs.

Get the length of a lisp sequence in lua:
#+Begin_SRC emacs-lisp
(setq v [1 2 3])
(lua-eval "len = #el.v") ;; the alternative is "elf.length(el.v)"
lua.len
==> 3
#+END_SRC
Getting and setting vector values from Lua (also works for lists and hashtables):
#+Begin_SRC emacs-lisp
(setq v [1 2 3])
(lua-eval "el.v[1] = 'new' .. el.v[1]")
v
==> [1 "new2" 3]
#+END_SRC
Iterate over a list: (also works with vectors and hashtables)
#+Begin_SRC emacs-lisp
(setq x '("one" "two" "three"))
(lua-eval "for k,v in pairs(el.x) do
el.x[k] = v .. k
end")
x
==> ("one0" "two1" "three2")
#+END_SRC
Lua function convert a lisp dictionary to an lisp alist
#+Begin_SRC emacs-lisp

(setq ht (make-hash-table))
(puthash 1 "first" ht)
(puthash 2 "second" ht)
(lua-eval "function to_alist(ht)
local list = nil
for k,v in pairs(ht) do
list = elf.cons(elf.list(k, v), list)
end
return list
end")
(setq alist (lua.to_alist ht))
alist
==> ((1 "first") (2 "second"))
#+END_SRC

** Working with lua tables with lisp primitives
The following primitives are provided:
lua-new-table, lua-get, lua-set, lua-rawset,
lua-setmetatable, alist-to-table, sequence-to-table.
Example:
#+Begin_SRC emacs-lisp
(setq tbl (lua-new-table)) ;;==> #
(setq mt (lua-new-table))
(lua-set mt "__index" mt)
(lua-set tbl "x" 3)
(lua-set mt "a" "in metatable")
(lua-setmetatable tbl mt)
(lua-get tbl "x") ==> 3
(lua-get tbl "a") ==> "in metatable"
#+END_SRC

** Creating Emacs commands in Lua
The lua function 'def_command' is used to define command callable from Emacs
with M-x.
#+Begin_SRC emacs-lisp
(lua-eval "function say_hi ()
elf.message('hello from lua')
end

def_command('say_hi', say_hi)
")
#+END_SRC
** extended examples
In the [[file:luamacs_examples/][luamacs_examples/]] directory:
- [[file:luamacs_examples/ascii-table.lua][ascii-table.lua]] Defines the Emacs command 'ascii_table'
- [[file:luamacs_examples/charland.lua][charland.lua]] An example game

* misc lisp primitives
- 'lua-garbage-collect': Runs the lua garbage collector
- 'lua-stacksize': Returns the size of the lua stack
* Running tests
#+Begin_SRC emacs-lisp
(require 'luamacs-tests)
#+END_SRC
#+Begin_SRC text
M-x luamacs-run-tests
#+END_SRC
* Limitations
- Calling lisp macros from Lua
- Lisp Symbols
- ...

Because of these limitations it is not practical to write a full Emacs mode in Lua
Without some Lisp support.
* Known bugs
- You cannot call the lisp function 'goto-char' from within a Coroutine.
(I have no idea why)
- Lua errors are unhandled often cause Emacs to crash
- Nested calls to lisp functions from Lua. This crashes Emacs: elf.a(elf.b())
- Crashes Emacs: (setq (lambda ()))
- ...
* Internals
** Data type conversions
The main conversion functions are defined in src/data.c they are 'lisp_to_lua'
and 'lua_to_lisp'. Numbers and strings are converted into native types while
a reference is created for everything else (references should probably also
be created for strings).

'setlispvalue' (src/lobject.h) is used to set the lisp object in a lua reference.
'build_lua_tvalue' (src/alloc.c) is used to create a lisp reference.

** Lisp references to lua objects
Lisp references are defined in src/lisp.h, they are made by extending
the lisp union Lisp_Misc with struct Lisp_Lua_TValue, which contains
a field TValue* referencing the lua value. This forms the type
Lisp_Misc_Lua_TValue.

Each lua object that is referenced from lisp contains a field that points
to its lisp reference. This way if a lua object is referenced again
from lisp the same reference object can be returned. When the reference
is garbage collected, it removes itself from lua object that it references.

** Lua references to lisp objects
Lua references, unlike the lisp ones, are not collectible. This stems
from a poor decision made when the structure of Lua data types was not
well understood.

The Lua references store the lisp object they refer to in the union Value
(src/lobject.h). The union GCObject should be extended instead instead.
Doing so would allow them to be collected in the same way as the lisp
references are. Currently, once a lisp object has been referenced
from lua, it will never be garbage collected.
** Accessing lisp data and functions from lua
This is all done through the tables 'el' and 'elf' defined using the
lua c API in src/luamacs.c
** Accessing lua data and functions from lisp
The lisp primitive 'symbol-value' is modified to intercept symbol value
lookups that use the pseudo lua object notation (lua.varname), it extracts
the variable name and looks it up in Lua instead. A similar thing is
done when calling lua functions from lisp, this time the function 'eval_sub'
and lisp primitive "funcall" in src/eval.c where modified.
** Garbage collection
Because objects from each of the interpreters can be referenced
by the other interpreter, some objects that are still reachable may not
get marked. To make all user-reachable objects GC-reachable, all
objects that are referenced by the other interpreter are inserted into a
hash table when a reference to them is created. When the reference is collected,
that object is removed from the hash table. The lua table that stores objects
referenced from lisp is '__lisp_references'. The lisp hashtable that
stores objects referenced from lua is '__referenced_from_lua'.