mua

High-level Lua bindings for Moonbit

lua
moon add tonyfettes/mua@0.1.1
Download zip
Version
0.1.1
License
Apache-2.0
Last updated
8 months ago
Downloads
19

Dependencies

README

#tonyfettes/mua

A high-level, safe embedding of Lua 5.4 for MoonBit, providing idiomatic interfaces for running Lua code, exchanging data between MoonBit and Lua, and managing Lua values with automatic memory management.

#Features

  • Safe Lua State Management: Automatic cleanup of Lua states through MoonBit's memory management
  • Type-Safe Value Exchange: High-level Value enum for seamless data conversion between MoonBit and Lua
  • Table Support: First-class support for Lua tables with iteration, indexing, and JSON conversion
  • Error Handling: Comprehensive error types using MoonBit's error handling system
  • Multiple Lua Types: Support for all Lua types including functions, userdata, threads, and light userdata
  • Standard Libraries: All Lua 5.4 standard libraries pre-loaded
  • Memory Safe: Uses registry references to keep Lua values alive across the FFI boundary

#Installation

Add tonyfettes/mua to your moon.mod.json:

moon update moon add tonyfettes/mua

#Quick Start

The Value enum represents all Lua types:

///|
enum Value {
Integer(Int64)
Number(Double)
String(String)
Boolean(Bool)
Nil
Thread(Lua)
Table(Table)
LightUserdata(LightUserdata)
Userdata(Userdata)
Function(Function)
}

#Creating a Lua State

///|
let lua : @mua.Lua = try! @mua.new()

#Evaluating Lua Code

// Simple evaluation
let results = lua.eval("return 1 + 2", [])
@json.inspect(results)

// With arguments
let results = lua.eval("return ... * 2", [Integer(21)])
@json.inspect(results)

// Multiple return values
let results = lua.eval("return 1, 'two', 3.0", [])
@json.inspect(results)

#Pushing and Getting Values

// Push a value onto the stack
lua.push(Integer(42))

// Get a value from the stack
let value = lua.get(-1)
@json.inspect(value, content=[42])

#Working with Tables

let results = lua.eval("return {1, 2, 3, x = 'hello'}", [])
match results[0] {
Table(table) => {
// Get table length
let len = table.length()
@json.inspect(len, content=3)

// Get value by key
let value = table.get(Integer(1))
@json.inspect(value, content=[1])
let x = table.get(String("x"))
@json.inspect(x, content=["hello"])

// Convert to array
let array = table.to_array()
@json.inspect(array, content=[1, 2, 3])

// Iterate over all key-value pairs
for k, v in table {
println("\{k} => \{v}")
}
}
_ => ()
}

#Error Handling

All Lua errors are captured as typed errors:

let _ = lua.eval("error('something went wrong')", []) catch {
err => {
// err is of type Err
match err {
RuntimeError(value) => println("Runtime error: \{value}")
SyntaxError(value) => println("Syntax error: \{value}")
MemoryError(value) => println("Memory error: \{value}")
_ => println("Other error")
}
[]
}
}

Error types include:

  • RuntimeError(Value) - Runtime errors
  • SyntaxError(Value) - Syntax errors during compilation
  • MemoryError(Value) - Memory allocation failures
  • MessageHandlerError(Value) - Errors in the message handler
  • FileError(Value) - File-related errors
  • UnknownError(Int, Value) - Unknown error codes

#Testing

Run the test suite:

moon test

Update test snapshots:

moon test --update

#Development

#Code Style

  • Code is organized in blocks separated by ///|
  • Run moon fmt to format code
  • Run moon info to update generated interfaces (.mbti files)
  • Run moon check for linting

#Building

moon check # Type check and lint moon test # Run tests moon info && moon fmt # Update interfaces and format

#Contributing

Contributions are welcome! Please ensure:

  1. All tests pass (moon test)
  2. Code is formatted (moon fmt)
  3. Interface files are updated (moon info)
  4. New features include tests

#License

Apache License 2.0 - See LICENSE for details.

#Acknowledgments

This project embeds Lua 5.4, a powerful, efficient, lightweight, embeddable scripting language.

#
Err

pub suberror Err {
RuntimeError(Value)
MemoryError(Value)
MessageHandlerError(Value)
SyntaxError(Value)
FileError(Value)
UnknownError(Int, Value)
}

Error type used by the high-level Lua API.

Each variant corresponds to a particular Lua status code and carries the underlying Lua error value as a [Value].
impl Show for Err
impl ToJson for Err

#
Function

type Function

Opaque handle to a Lua function stored in the registry.

Values of this type can be obtained from the stack through [Value::Function] and are represented by a registry reference.
impl Show for Function
impl ToJson for Function

#
LightUserdata

type LightUserdata

Lightweight wrapper around a raw C pointer exposed to Lua.

This corresponds to Lua's lightuserdata type and does not participate in garbage collection on the Lua side.

#
Lua

type Lua

High-level handle to an embedded Lua 5.4 state.

Each Lua value wraps a lua_State* pointer and owns the lifetime of that state. When the Lua value is collected, the underlying state is automatically closed.
impl Show for Lua
impl ToJson for Lua

#
Lua::eval

fn Lua::eval(self : Lua, code : String, args : Array[Value]) -> Array[Value] raise Err

Loads and runs a Lua chunk with the given arguments.

The code string is compiled as a Lua chunk and called with the values from args as arguments. All return values are collected from the stack and returned as an array of Value.

Raises Err if compilation or execution fails.

#
Lua::get

fn Lua::get(self : Lua, index : Int) -> Value?

Reads a value from the Lua stack at the given index.

Returns None if the index is invalid or the slot does not contain a value that can be represented as [Value].

#
Lua::new

#as_free_fn
fn Lua::new() -> Lua raise Err

Creates a new Lua state with all standard libraries loaded.

This is the preferred entry point for embedding Lua. It configures the allocator so that the state is closed automatically when the returned Lua value is collected.

#
Lua::push

fn Lua::push(self : Lua, value : Value) -> Unit

Pushes a high-level [Value] onto this state's Lua stack.

Tables, userdata, and functions are pushed by looking up their internal registry reference; other variants are converted to the corresponding Lua primitive type.

#
Table

type Table

Opaque handle to a Lua table stored in the registry.

Instances of this type are created when converting Lua values into [Value::Table] and keep the underlying table alive via a registry reference.
impl Show for Table
impl ToJson for Table

#
Table::get

fn Table::get(self : Table, value : Value) -> Value?

#
Table::iterator2

fn Table::iterator2(self : Table) -> Iterator2[Value, Value]

Returns an iterator over this table's key-value pairs.

The iterator traverses the table using lua_next, yielding arbitrary keys and values as [Value] pairs until the table is exhausted.

#
Table::length

fn Table::length(self : Table) -> Int

Computes the length of this table using Lua's # operator.

This is equivalent to calling @lua.len on the underlying table and converting the result to a MoonBit Int.

#
Table::to_array

fn Table::to_array(self : Table) -> Array[Value]

#
Userdata

type Userdata

Opaque handle to full Lua userdata stored in the registry.

Instances of this type refer to heap-allocated blocks managed by Lua and kept alive through a registry reference.
impl Show for Userdata
impl ToJson for Userdata

#
Value

pub(all) enum Value {
Integer(Int64)
Number(Double)
String(String)
Boolean(Bool)
Nil
Thread(Lua)
Table(Table)
LightUserdata(LightUserdata)
Userdata(Userdata)
Function(Function)
}

High-level tagged representation of Lua values.

Values of this enum are used to move data between MoonBit code and the underlying Lua state without exposing raw C pointers.
impl Show for Value
impl ToJson for Value

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io