A Lisp to Lua compiler, compatible with LuaJIT or Lua5.1+. Lua 5.2+ or LuaJIT (built with -DLUAJIT_ENABLE_LUA52COMPAT) recommended for higher performance.
This project is currently very unstable. Every commit is more likely than not to break compatibility with its previous commit. If you find a commit that works for you, stick to it. Right now I'm working on rewriting large parts of it to improve parsing performance, as well as adding a parser generator that will be used to parse Lua inside Lisp code, and adding meta information to compiled expressions so it'll be possible to intercept Lua runtime errors and translate them back into a Lisp traceback.
- Reader Macros
- Macros
- Lua functions
- Compiler modification on-the-fly during compile-time.
- Lisp in web server. [Nginx] [Apache]
- Lisp in Redis. [Redis]
- Lisp with Lua web framework. [Lapis]
- Lisp with Lua game engine. [LÖVE]
- Lisp in Lua scripted game. [Wesnoth]
- Cross-platform mobile app development with Lisp. [Corona] [MOAI]
- Lisp whenever there's only ANSI C to build Lua with.
- Where there's Lua, there can be Lisp also.
Play around. Make issues. Submit pull requests. :)
./bin/l2l --enable read_execute
to launch REPL.
;; Welcome to Lisp-To-Lua REPL!
;; Type '(print "hello world!") to start.
>> (print "Hello world!")
Hello world!
= nil
>>
./bin/l2l --enable read_execute sample01.lisp
to compilesample01.lisp
and output Lua to stdout../bin/l2l --enable read_execute sample01.lisp | lua
to compile and runsample01.lisp
immediately../bin/l2l sample02.lisp sample03.lisp
to compile two lisp files where one requires the other.make -C sample04
to run the makefile in the sample04 directory. It demonstrates how to use l2l from another directory.make samples
to build all samples. Requires bash.make test
to run unit tests.make repl
to launch the repl.
bin/l2l
;; Welcome to Lisp-To-Lua REPL!
;; Type '(print "hello world!") to start.
>> (import sample02) ;; sample02.lisp defines the if1 macro.
= true
>> (if1 false 1 2)
= 2
lua
Lua 5.2.4 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> require("l2l.eval").loadstring("(print 1) (print 2)")()
1
2
>
While l2l is not a Scheme or Common Lisp implementation, it shares many features of these languages.
It is a Lisp-1
like Scheme, which means that functions are treated like other values
rather than being stored in a separate namespace. However, the macro
system uses a much simpler defmacro
like CL and Clojure instead of
Scheme's hygenic macros.
The let
macro for binding locals works more like Clojure--it does
not require each name/value pair to be wrapped in its own parens:
(let (x 12
y 30)
(+ x (* y 22)))
The syntax for varargs is taken from Lua rather than any existing lisp dialect; it uses three dots:
(defun myfun (...) (cdr (pack ...)))
Functions can also return multiple values.
(defun x () (id 1 2 3))
(print (x))
(set y (vector (x))) ;; wrap it in a table.
;; prints "1 2 3"
The -
and /
operators do not have a unary mode.
(- 4)
and
(/ 4)
both return 4.
Implementing unary mode would prevent implementing these two operators directly
in the form of (a - b - c - d...)
and (a / b / c / d)
.
There are complications that can arise because of Lua's vararg mechanics.
Should (- (somefunction x))
be a unary call or a non-unary call?
somefunction
can return 1 or more values, and it is impossible to know which,
in the compiling stage, before the particular call is evaluated.
- Change prompt string by setting the
_P
global variable.
>> (set _P ">->o ")
= >>
>->o (print "Hello World")
Hello World
= nil
>->o
- The read macro table is
_R
._R.META
stores locations of all read symbols.
>> (set _R.META {}) ;; _R.META is too big.
= table: 0x7fcf90c54c90
>> (show _R)
= {"}" function: 0x7fcf90c1a930 "META" {(show _R) {1 6 2 9 0 6}} ";" function: 0x7fcf90c1a8c0 "position" function: 0x7fcf90c1b1a0 ")" function: 0x7fcf90c1a8e0 "(" function: 0x7fcf90c1ac10 "'" function: 0x7fcf90c1ad30 "," function: 0x7fcf90c1adb0 "[" function: 0x7fcf90c1aab0 "#" function: 0x7fcf90c1adf0 "\"" function: 0x7fcf90c1a9d0 "]" function: 0x7fcf90c1a980 "`" function: 0x7fcf90c1ad70 "{" function: 0x7fcf90c1ab80}
>>
- The dispatch read macro table is
_D
.
>> (show _D)
= {"." function: 0x7fcf90c27360 " " function: 0x7fcf90c1a890 "'" function: 0x7fcf90c1acf0}
- The macro table is
_M
.
>> (defmacro GAMMA () '(+ 1 2))
= function: 0x7f8fe3e52e00
>> (show _M)
= {"GAMMA" function: 0x7f8fe3e52e00}
- The compiler table is
_C
.
>> (show _C)
= {"quote" function: 0x7f8fe3e0f790 "lambda" function: 0x7f8fe3e16930 "_119_104_105_108_101" function: 0x7f8fe3f3a470 "defun" function: 0x7f8fe3e16a90 "_97_110_100" function: 0x7f8fe3e21ae0 "_58" function: 0x7f8fe3e22300 "_110_111_116" function: 0x7f8fe3e0ecf0 "_62_61" function: 0x7f8fe3e1acf0 "_" function: 0x7f8fe3e22160 "_37" function: 0x7f8fe3e222a0 "defmacro" function: 0x7f8fe3e19f10 "let" function: 0x7f8fe3e16a20 "car" function: 0x7f8fe3e16990 "chunk" function: 0x7f8fe3f5ce00 "_61_62" function: 0x7f8fe3e16930 "_60" function: 0x7f8fe3e20ce0 "_35" function: 0x7f8fe3e22340 "cond" function: 0x7f8fe3e0f820 "defcompiler" function: 0x7f8fe3e16ac0 "_102_111_114" function: 0x7f8fe3f58650 "_100_111" function: 0x7f8fe3f1b1c0 "_98_114_101_97_107" function: 0x7f8fe3f40e10 "quasiquote" function: 0x7f8fe3e0f7d0 "_42" function: 0x7f8fe3e1f030 "_47" function: 0x7f8fe3e1db00 "_62" function: 0x7f8fe3e1abe0 "_43" function: 0x7f8fe3e0d7c0 "_46_46" function: 0x7f8fe3e10a90 "_61" function: 0x7f8fe3e22370 "_61_61" function: 0x7f8fe3e1a5f0 "set" function: 0x7f8fe3e22370 "cdr" function: 0x7f8fe3e169c0 "cadr" function: 0x7f8fe3e169f0 "table_quote" function: 0x7f8fe3e0f750 "_60_61" function: 0x7f8fe3e14e60 "_105_102" function: 0x7f8fe3e0f8c0 "_111_114" function: 0x7f8fe3e159c0 "_46" function: 0x7f8fe3e222d0}
-
A "compiler" can be used to implement special forms.
-
The format of a compiler is a function with at least two arguments. For example:
local function compile_car(block, stream, form)
return "(("..compile(block, stream, form) .. ")[1])"
end
This implements compiling (car x)
to (x[1])
.
A compiler function inserts any non-expression Lua statements into block
,
and returns a single Lua expression which should either be the value
or reference to the value that will be returned in its parent lisp block
(which is likely to be a lisp function).
The arguments of a function are raw lisp values, uncompiled and unprocessed. They must be compiled before being inserted into generated Lua code.
There are caveats involved when implementing special forms involving
variadic arguments, since the compiler function would see ...
rather
than the expanded values. Checkout the math operators in "compilers.lua".
- Use the
defcompiler
helper to define compilers in lisp. For example:
(defcompiler -- (block stream str)
(table.insert block (.. "\n--" (tostring str))))
This implements comments that will be printed directly into the Lua output.
defcompiler
will put your compiler into _C
table as well as activate
the compiler immediately for use. Right after the above compiler
declaration you can have:
(-- "This is a comment")
and the code will be output directly as "-- This is a comment" into the Lua source code.
- Make sure
_R.META
is recording locations accurately enough during the compiler stage. Implement a method to automate unwrapping of...
arguments to operators.compiler.lua
self-bootstrapping generates ugly code and poses problem when sandboxing.- Replace the io interface
reader.lua
uses with one that has nothing to do with files.
Copyright © 2012-2015, Eric Man and contributors Released under the 2-clause BSD license, see LICENSE