lua
is a Lua embedding library for Carp. It provides
two modules: Lua wraps the Lua C API directly, and Luax builds a safer
interface on top using Maybe and Result types.
Installation
(load "https://github.com/carpentry-org/lua@0.1.0")
(Lua.setup "lua")
; if your library is named differently (e.g. lua5.4):
(Lua.setup "lua" "lua5.4")
Getting started
All work happens inside a Lua.with-lua-do block, which creates a Lua state
bound to lua and closes it when the block exits. Call Lua.libs to load the
Lua standard library.
(defn main []
(Lua.with-lua-do
(Lua.libs lua)
(ignore (Lua.do-string lua (cstr "print('hello from lua')")))))
The Lua C API is stack-based: you push values onto a virtual stack, run operations, and read results back using negative indices (-1 is the top).
(Lua.push-int lua 42)
(Lua.push-float lua 3.14f)
(let [f (Lua.get-float lua -1) ; 3.14
i (Lua.get-int lua -2)] ; 42
(Lua.pop lua 2))
Calling Lua functions
Lua.fun defines a Lua function from inline source. Luax.call-fn handles the
full call cycle — looking up the function, pushing arguments, calling, and
reading the result — and returns Result to catch undefined functions and
runtime errors:
(ignore (Lua.fun lua add [x y] "return x + y"))
(match (Luax.call-fn lua add Lua.get-int
(Lua.push-int 3) (Lua.push-int 4))
(Result.Success v) (IO.println &(fmt "3 + 4 = %d" v))
(Result.Error e) (IO.errorln &e))
Working with tables
Luax.make-table creates a table, sets fields, and assigns it to a global in
one expression. Field values are given as push expressions:
(Luax.make-table lua player
(name (Lua.push-carp-str "Ada"))
(hp (Lua.push-int 100))
(x (Lua.push-float 3.5f)))
To read fields back, push the table and use Luax.get-*-field. These return
Maybe and pop the field value internally, so you can read multiple fields in
sequence without manual stack management:
(Lua.get-global lua (cstr "player"))
(match (Luax.get-string-field lua -1 "name")
(Maybe.Just name) (IO.println &name)
(Maybe.Nothing) (IO.errorln "no name"))
(match (Luax.get-int-field lua -1 "hp")
(Maybe.Just hp) (IO.println &(fmt "hp: %d" hp))
(Maybe.Nothing) (IO.errorln "no hp"))
(Lua.pop lua 1)
Loading Lua files
Use Lua.eval-file to load and execute a Lua file with error handling:
(match (Lua.eval-file lua "config.lua")
(Result.Success _) (IO.println "loaded")
(Result.Error e) (IO.errorln &e))
Safe globals
Luax.set-*-global and Luax.get-*-global handle push/pop for you. The
getters return Maybe and leave the stack clean:
(Luax.set-int-global lua "score" 100)
(match (Luax.get-int-global lua "score")
(Maybe.Just n) (IO.println &(fmt "score: %d" n))
(Maybe.Nothing) (IO.errorln "not found"))