lua

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2026 License: MIT Imports: 25 Imported by: 0

README

Lunar gopher orbiting the Moon

Lunar

A fast, memory-efficient Lua 5.1 runtime for Go.

Go Reference MIT License

Lunar implements Lua 5.1, plus Lua 5.2-style goto and labels, entirely in Go: compiler, bytecode VM, coroutines, binary chunks, and standard libraries. Its compiler, VM, libraries, and pattern engine are checked against the Lua 5.1 reference implementation.

Quick start

package main

import (
	"fmt"

	"github.com/mmcdole/lunar"
)

func main() {
	state, err := lua.New(lua.Options{
		Libraries: lua.LibrarySet{
			lua.BaseLibrary,
			lua.StringLibrary,
			lua.TableLibrary,
		},
	})
	if err != nil {
		panic(err)
	}
	defer state.Close()

	greet, err := state.NewNativeFunction(func(frame lua.Frame) lua.Outcome {
		name, ok := frame.String(0)
		if !ok {
			frame.ThrowArgTypeError(0, lua.StringKind)
		}
		return frame.ReturnString("hello, " + name)
	})
	if err != nil {
		panic(err)
	}
	if err := state.SetGlobal("greet", greet.Value()); err != nil {
		panic(err)
	}

	results, err := state.DoString(
		"@hello.lua",
		`return greet("world"):upper()`,
	)
	if err != nil {
		panic(err)
	}

	greeting, _ := results[0].AsString()
	fmt.Println(greeting)
}

The library list is an allow-list. Its zero value installs no standard libraries; the example chooses only base, string, and table. :upper() works because StringLibrary installed the string metatable. Use lua.CoreLibraries() for the usual capability-safe profile, lua.FullLibraries() for trusted scripts, or an exact lua.LibrarySet like the example. Individual Open* methods remain available for deliberate later grants.

DoString loads source supplied directly by Go. Named script-file loading is a separate permission: ScriptLoader defaults to denied, while HostLoader, FSLoader, and FuncLoader explicitly select where scripts may come from.

See Embedding Lunar for callbacks, tables, contexts, errors, coroutines, and lifecycle management.

Performance

These results are medians from 15 runs on an Apple M3 Pro with Go 1.25.1 at source revision 1d43aec. Lower is better.

Established Lua program Lunar GopherLua go-lua
binary-trees 162.2 ms 172.4 ms 179.8 ms
fannkuch-redux 23.63 ms 33.17 ms 40.14 ms
n-body 59.03 ms 193.39 ms 197.77 ms
spectral-norm 52.82 ms 160.14 ms 153.66 ms
Embedding operation Lunar GopherLua go-lua
Go calls Lua with scalar arguments 61.21 ns 61.51 ns 146.80 ns
Lua calls Go 1,000 times 62.49 µs 99.58 µs 84.37 µs
Lua echoes a 128-byte Go string 86.20 ns 81.41 ns 144.00 ns
Lua checksums a reused Go-built table 312.4 ns 578.3 ns 972.9 ns
Build a table in Go, then checksum it in Lua 2.230 µs 1.405 µs 1.645 µs
Loading a 9 MB CBOR graph Lunar GopherLua
Total memory allocated while loading 107.5 MB 784.6 MB
Live heap added after garbage collection 72.24 MiB 542.26 MiB

The full results include confidence intervals, allocation counts, and raw output. The benchmark protocol lists the commands, inputs, and runtime versions.

Compatibility

Lunar GopherLua Shopify go-lua
Lua version Lua 5.1 with Lua 5.2-style goto Lua 5.1 with Lua 5.2-style goto Lua 5.2
Go API Functions return typed values; callbacks use typed Frame accessors Values are LValue objects; callbacks pass arguments and results through an LState stack Mirrors the Lua C API; values are addressed by numeric stack position
Libraries in a new state None by default; select any subset at construction or open one later All standard libraries None; call OpenLibraries or open them individually
Script-file loading Denied by default; select host files, an fs.FS, or a host function Ambient OS file access Ambient OS file access when the applicable libraries are open
Coroutines Supported from Lua and Go Supported from Lua and Go Not implemented
Cancellation One installed context covers execution, loading, and coroutines One context on the state, execution only No context-based cancellation
os.exit Returns an *lua.ExitRequest to Go Exits the entire Go process Exits the entire Go process
Binary chunks Reads and writes Lua 5.1 bytecode when byte order and type sizes match Cannot read or write standard Lua bytecode files Reads and writes Lua 5.2 bytecode through the Go API

Scope

The compiler, VM, standard libraries, coroutines, binary chunks, weak tables, and finalizers are implemented. Current intentional limits are:

  • no C ABI, native C-module loading, or light userdata;
  • no debug.sethook or debug.gethook;
  • garbage collection runs synchronously rather than incrementally;
  • no deterministic VM-instruction budget; and
  • no high-level table iteration convenience beyond the precise Table.Next primitive.

One State can execute one goroutine at a time. Separate States may run concurrently.

The public embedding API is still stabilizing.

Documentation

  • Embedding: setup, calls, callbacks, values, contexts, errors, and lifecycle
  • Architecture: compiler, VM, runtime representation, and API boundaries
  • Language compatibility: the goto extension and intentional Lua 5.1/5.2/LuaJIT choices
  • Collection: Lua reachability, weak tables, and finalization
  • Performance: measurement groups and regression policy
  • Third-party notices: adapted algorithms, artwork, and benchmark sources

Lunar is available under the MIT License.

Documentation

Overview

Package lua implements the Lua 5.1 language and a compact, typed Go embedding interface.

Example
state, err := lua.New(lua.Options{
	Libraries: lua.LibrarySet{
		lua.BaseLibrary,
		lua.StringLibrary,
		lua.TableLibrary,
	},
})
if err != nil {
	panic(err)
}
defer func() {
	if err := state.Close(); err != nil {
		panic(err)
	}
}()

greet, err := state.NewNativeFunction(func(frame lua.Frame) lua.Outcome {
	name, ok := frame.String(0)
	if !ok {
		frame.ThrowArgTypeError(0, lua.StringKind)
	}
	return frame.ReturnString("hello, " + name)
})
if err != nil {
	panic(err)
}
if err := state.SetGlobal("greet", greet.Value()); err != nil {
	panic(err)
}

results, err := state.DoString(
	"@hello.lua",
	`return greet("world"):upper()`,
)
if err != nil {
	panic(err)
}
greeting, _ := results[0].AsString()
fmt.Println(greeting)
Output:
HELLO, WORLD

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrScriptLoadingDisabled reports an attempt to open a script through a
	// State whose ScriptLoader grants no script-file access.
	ErrScriptLoadingDisabled = errors.New(
		"lua: script-file loading is disabled",
	)

	// ErrNilScriptFS reports FSLoader called with a nil filesystem.
	ErrNilScriptFS = errors.New("lua: nil script filesystem")

	// ErrNilScriptOpener reports FuncLoader called with a nil opener.
	ErrNilScriptOpener = errors.New("lua: nil script opener")
)
View Source
var ErrCapacity = errors.New("lua: capacity hint is too large")

ErrCapacity reports a collection capacity hint too large for eager allocation. Tables may still grow beyond this size incrementally.

View Source
var ErrClosed = errors.New("lua: state is closed")

ErrClosed reports an operation that requires a live State.

View Source
var ErrForeignValue = errors.New("lua: value belongs to another state")

ErrForeignValue reports a reference value owned by another State.

View Source
var ErrInvalidKey = errors.New("lua: invalid table key")

ErrInvalidKey reports nil, NaN, or another invalid Lua table key.

View Source
var ErrInvalidLibrary = errors.New("lua: invalid standard library")

ErrInvalidLibrary reports an unknown standard-library value in a construction-time LibrarySet.

View Source
var ErrInvalidNativeFunction = errors.New("lua: native function is nil")

ErrInvalidNativeFunction reports construction with a nil native entry.

View Source
var ErrInvalidNextKey = errors.New("lua: invalid key to next")

ErrInvalidNextKey reports a key that is not a valid continuation for table traversal.

View Source
var ErrInvalidPrototype = errors.New("lua: invalid prototype")

ErrInvalidPrototype reports a nil or invalid Prototype.

View Source
var ErrInvalidUserDataType = errors.New(
	"lua: invalid userdata type descriptor",
)

ErrInvalidUserDataType reports use of a zero or otherwise invalid UserDataType descriptor.

View Source
var ErrInvalidUserDataTypeName = errors.New(
	"lua: userdata type name is empty",
)

ErrInvalidUserDataTypeName reports an empty userdata type name.

View Source
var ErrInvalidValue = errors.New("lua: invalid value")

ErrInvalidValue reports use of the zero Value.

View Source
var ErrMainThread = errors.New("lua: main thread cannot be resumed")

ErrMainThread reports an attempt to resume a State's main Thread through the coroutine interface.

View Source
var ErrNativeCaptureLimit = errors.New("lua: native function capture limit exceeded")

ErrNativeCaptureLimit reports a native function with more than 255 captures.

View Source
var ErrNegativeCapacity = errors.New("lua: capacity hint is negative")

ErrNegativeCapacity reports a negative collection capacity hint.

View Source
var ErrNilContext = errors.New("lua: nil context")

ErrNilContext reports a nil context passed to SetContext.

View Source
var ErrNilReader = errors.New("lua: nil source reader")

ErrNilReader reports a nil Reader passed to Load or returned by a ScriptOpener.

View Source
var ErrReadOnlyUserData = errors.New(
	"lua: runtime-owned userdata payload is read-only",
)

ErrReadOnlyUserData reports an attempt to replace the payload of userdata reserved for a runtime library's native resource.

View Source
var ErrRunning = errors.New("lua: state is executing")

ErrRunning reports an operation that cannot run while Lua is executing.

View Source
var ErrUnsupportedTreeValue = errors.New(
	"lua: unsupported Go value in table tree",
)

ErrUnsupportedTreeValue reports a Go value that NewTableFrom cannot convert.

View Source
var ErrUserDataTypeConflict = errors.New(
	"lua: userdata type name has a conflicting Go type",
)

ErrUserDataTypeConflict reports reuse of a userdata type name with a different Go payload type.

Functions

This section is empty.

Types

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error is a protected execution failure.

It owns the original Lua Value and a compact traceback snapshot. Formatting never invokes Lua, tostring, or a metamethod, so Error remains safe after the owning State closes.

func (*Error) Category

func (err *Error) Category() ErrorCategory

Category returns the broad error category.

func (*Error) Error

func (err *Error) Error() string

Error returns a stable non-executing description.

func (*Error) Traceback

func (err *Error) Traceback() []TraceFrame

Traceback returns an owned copy of the traceback.

func (*Error) Unwrap

func (err *Error) Unwrap() error

Unwrap returns the underlying Go cause, if any.

func (*Error) Value

func (err *Error) Value() Value

Value returns the arbitrary Lua error Value.

type ErrorCategory

type ErrorCategory uint8

ErrorCategory classifies an Error without replacing its arbitrary Lua error Value.

const (
	// RuntimeError identifies an error raised while executing Lua.
	RuntimeError ErrorCategory = iota
	// SyntaxError identifies source or bytecode rejected before execution.
	SyntaxError
	// ResourceError identifies a deterministic call or execution
	// resource-limit failure. It remains an ordinary Lua error; the category
	// is additional information for Go callers.
	ResourceError
	// ContextError identifies cancellation or deadline expiry requested by
	// the host. Lua protected calls do not catch it.
	ContextError
	// ExitError identifies an os.exit request returned to the host. Lua
	// protected calls do not catch it.
	ExitError
	// LimitError identifies a host ceiling configured through Options, such
	// as MaxHeapBytes. Unlike ResourceError, which reports a limit Lua 5.1
	// itself defines and a script may legitimately recover from, a host
	// ceiling exists to be enforced against the script: Lua protected calls
	// do not catch it, and it ends the outer operation.
	LimitError
)

type ExitRequest

type ExitRequest struct {
	// contains filtered or unexported fields
}

ExitRequest reports that Lua called os.exit.

Lunar never terminates the Go process itself. An os.exit call returns an *Error that unwraps to *ExitRequest, allowing the application to apply its own process, service, or request-lifecycle policy.

func (*ExitRequest) Error

func (request *ExitRequest) Error() string

Error returns a stable host-facing description of the request.

func (*ExitRequest) ExitCode

func (request *ExitRequest) ExitCode() int

ExitCode returns the status supplied to os.exit.

type Frame

type Frame struct {
	// contains filtered or unexported fields
}

Frame is a borrowed view of one native call.

Argument indexes are zero-based. Typed argument methods perform exact Lua type checks and do not coerce values. Owning Values and object handles read from a Frame may be retained, but the Frame itself is valid only until a terminal Return, Raise, ArgError, or Yield method is called, or until the NativeFunc returns.

func (Frame) Argument

func (frame Frame) Argument(index int) (Value, bool)

Argument returns argument index as an owning Value and whether it was supplied. A missing argument returns Lua nil and false. A negative index is a programming error.

func (Frame) ArgumentCount

func (frame Frame) ArgumentCount() int

ArgumentCount returns the number of supplied arguments.

func (Frame) Bool

func (frame Frame) Bool(index int) (bool, bool)

Bool returns argument index and whether it is exactly a Lua boolean.

func (Frame) Call

func (frame Frame) Call(
	callable Value,
	arguments ...Value,
) ([]Value, error)

Call invokes callable synchronously and in protected mode on the Thread executing frame.

Callable may be a Function or a value with a Function-valued __call metamethod. Results are owning Values and remain valid after this callback returns. Invalid or foreign inputs are rejected before Lua executes. Execution failures are returned as *Error, and Lua-visible side effects are not rolled back. ExitError is terminal for the enclosing public execution: after a nested call observes one, later Call, Index, or SetIndex operations return that first request and a callback's ordinary Outcome cannot suppress it.

A Go panic from the nested call propagates after the outer Frame is restored. A yield on this same Thread returns Lua 5.1's illegal-yield error; a separate child coroutine resumed by the call may yield normally.

The borrowed Frame remains valid after Call returns. It must not be used concurrently or from a callback entered by this call.

func (Frame) CallDiscard

func (frame Frame) CallDiscard(
	callable Value,
	arguments ...Value,
) error

CallDiscard invokes callable like Call and discards all results.

func (Frame) CallInto

func (frame Frame) CallInto(
	callable Value,
	arguments []Value,
	destination []Value,
) (count int, err error)

CallInto invokes callable synchronously and in protected mode, writing its results into destination.

Arguments are staged before execution, so arguments and destination may overlap. On success, count entries are written and the destination tail is unchanged. On an execution failure or input error, destination is unchanged and count is zero. If Lua produces more than len(destination) results, count is the required size and a *ResultCapacityError is returned; destination is unchanged, but Lua side effects have already occurred.

CallInto otherwise follows Call. When the target itself does not allocate, a warm call with sufficient internal capacity and caller-provided result storage adds no boundary allocation. The borrowed Frame remains valid after return.

func (Frame) CallOne

func (frame Frame) CallOne(
	callable Value,
	arguments ...Value,
) (Value, error)

CallOne invokes callable like Call and applies Lua's one-result adjustment. A call with no results returns Nil; extra results are discarded.

func (Frame) CoerceNumber

func (frame Frame) CoerceNumber(index int) (float64, bool)

CoerceNumber returns argument index as a Lua number.

Exact numbers pass through. Strings are accepted only when their complete contents match Lunar's deterministic Lua numeric grammar. No metamethod is invoked.

func (Frame) CoerceString

func (frame Frame) CoerceString(index int) (string, bool)

CoerceString returns argument index as a Lua string.

Exact strings pass through and numbers use Lua's primitive number spelling. Other kinds are rejected and no metamethod is invoked.

func (Frame) Collect

func (frame Frame) Collect() error

Collect performs one complete semantic collection from a NativeFunc and runs pending userdata finalizers before returning. Finalizers may execute arbitrary non-yielding Lua. Collect resumes automatic collection after success. A finalizer's Lua error is returned as an *Error through the error interface.

func (Frame) Context

func (frame Frame) Context() context.Context

Context returns the context governing this native callback.

It is the context installed with SetContext, or context.Background when none is installed. The Context may be retained after the callback returns; the borrowed Frame may not.

func (Frame) CurrentThread

func (frame Frame) CurrentThread() *Thread

CurrentThread returns the Thread executing this callback.

func (Frame) Equal

func (frame Frame) Equal(left, right Value) (bool, error)

Equal applies ordinary Lua equality from a native callback.

func (Frame) Function

func (frame Frame) Function(index int) (*Function, bool)

Function returns argument index and whether it is exactly a function.

func (Frame) Global

func (frame Frame) Global(name string) (Value, error)

Global applies ordinary Lua indexing to the executing Thread's global environment.

func (Frame) Index

func (frame Frame) Index(target, key Value) (Value, error)

Index applies ordinary Lua indexing from a native callback.

A raw table hit returns directly. Otherwise Index follows the bounded __index chain and may synchronously invoke Lua. Invalid or foreign Values are rejected before Lua executes. Execution failures are returned as *Error; a yield across this native-call boundary becomes Lua 5.1's illegal-yield failure. The borrowed Frame remains valid after Index returns.

func (Frame) Integer

func (frame Frame) Integer(index int) (int64, bool)

Integer returns argument index as an int64 when it is exactly a finite, integral Lua number representable by int64.

Integer does not accept numeric strings, truncate fractions, or saturate values outside the int64 range.

func (Frame) IntegerInRange

func (frame Frame) IntegerInRange(
	index int,
	minimum int64,
	maximum int64,
) (int64, bool)

IntegerInRange returns argument index as an int64 when Integer accepts it and it lies in the inclusive range [minimum, maximum].

An inverted range rejects every value.

Example
state, err := lua.New(lua.Options{})
if err != nil {
	panic(err)
}
defer state.Close()

describe, err := state.NewNativeFunction(func(frame lua.Frame) lua.Outcome {
	label, ok := frame.CoerceString(0)
	if !ok {
		frame.ThrowArgTypeError(
			0,
			lua.StringKind,
			lua.NumberKind,
		)
	}

	limit := int64(25)
	if !frame.IsMissingOrNil(1) {
		limit, ok = frame.IntegerInRange(1, 1, 100)
		if !ok {
			frame.ThrowArgError(
				1,
				"integer from 1 through 100 expected",
			)
		}
	}
	return frame.ReturnString(fmt.Sprintf("%s:%d", label, limit))
})
if err != nil {
	panic(err)
}

results, err := state.Call(describe.Value(), lua.Number(42))
if err != nil {
	panic(err)
}
text, _ := results[0].AsString()
fmt.Println(text)
Output:
42:25

func (Frame) IsMissingOrNil

func (frame Frame) IsMissingOrNil(index int) bool

IsMissingOrNil reports whether argument index was omitted or is Lua nil.

It is the common primitive for optional arguments: initialize a Go default, then read and validate the argument only when IsMissingOrNil returns false.

func (Frame) Kind

func (frame Frame) Kind(index int) Kind

Kind returns the exact Lua kind of argument index. A missing argument has InvalidKind, which distinguishes it from an explicit Lua nil.

func (Frame) Len

func (frame Frame) Len(value Value) (Value, error)

Len applies Lua's length operator from a native callback and preserves an arbitrary __len result.

func (Frame) Number

func (frame Frame) Number(index int) (float64, bool)

Number returns argument index and whether it is exactly a Lua number.

func (Frame) Rethrow

func (frame Frame) Rethrow(failure *Error)

Rethrow propagates a *Error returned by a nested Frame operation without losing its Value, category, or nested traceback, and does not return. See Throw.

func (Frame) Return

func (frame Frame) Return() Outcome

Return completes the callback without results.

func (Frame) ReturnArguments

func (frame Frame) ReturnArguments() Outcome

ReturnArguments completes the callback by returning every supplied argument in order without materializing it as owning Values.

func (Frame) ReturnBool

func (frame Frame) ReturnBool(value bool) Outcome

ReturnBool completes the callback with one Lua boolean result.

func (Frame) ReturnNil

func (frame Frame) ReturnNil() Outcome

ReturnNil completes the callback with one Lua nil result.

func (Frame) ReturnNumber

func (frame Frame) ReturnNumber(value float64) Outcome

ReturnNumber completes the callback with one Lua number result.

func (Frame) ReturnString

func (frame Frame) ReturnString(value string) Outcome

ReturnString completes the callback with one Lua string result.

func (Frame) ReturnValue

func (frame Frame) ReturnValue(value Value) Outcome

ReturnValue completes the callback with one owning Value.

func (Frame) ReturnValues

func (frame Frame) ReturnValues(values ...Value) Outcome

ReturnValues completes the callback with values.

Values are validated before any result slot is changed. The caller's requested result count is applied before the compact result window is written.

func (Frame) SetGlobal

func (frame Frame) SetGlobal(name string, value Value) error

SetGlobal applies an ordinary Lua assignment to the executing Thread's global environment.

func (Frame) SetIndex

func (frame Frame) SetIndex(target, key, value Value) error

SetIndex applies an ordinary Lua table assignment from a native callback.

An existing table field is replaced directly. Otherwise SetIndex follows the bounded __newindex chain and may synchronously invoke Lua. Invalid or foreign Values are rejected before Lua executes. Execution failures are returned as *Error; a yield across this native-call boundary becomes Lua 5.1's illegal-yield failure. The borrowed Frame remains valid after SetIndex returns.

func (Frame) State

func (frame Frame) State() *State

State returns the State executing this callback.

func (Frame) String

func (frame Frame) String(index int) (string, bool)

String returns argument index and whether it is exactly a Lua string.

func (Frame) Table

func (frame Frame) Table(index int) (*Table, bool)

Table returns argument index and whether it is exactly a Lua table.

func (Frame) Thread

func (frame Frame) Thread(index int) (*Thread, bool)

Thread returns argument index and whether it is exactly a Lua thread.

func (Frame) Throw

func (frame Frame) Throw(value Value)

Throw raises an arbitrary Lua error Value and does not return.

A native callback ends in exactly one of three ways: it returns a Return* Outcome, it returns a Yield* Outcome, or it throws. Throwing rather than returning a failure lets a helper called at any depth inside the callback report the error, which is where argument checks usually live.

Throw unwinds with a private panic that Lunar recovers at the native call boundary, so the thrown callback reaches that boundary in the same state a returned one would. Host code between the Throw and the NativeFunc must not recover it; a deferred recover that swallows unknown panics strands the callback and is reported as an invalid outcome.

Throw and its siblings return nothing, so they are written as statements. A guard clause reads "if !ok { frame.ThrowArgTypeError(0, lua.StringKind) }" and the callback continues below it only when the check passed.

func (Frame) ThrowArgError

func (frame Frame) ThrowArgError(index int, reason string)

ThrowArgError raises a Lua argument error and does not return.

index is zero-based. It may name a missing argument. See Throw.

func (Frame) ThrowArgTypeError

func (frame Frame) ThrowArgTypeError(index int, expected ...Kind)

ThrowArgTypeError raises a Lua argument-type error and does not return.

index is zero-based and may name a missing argument. At least one distinct expected kind is required. See Throw.

func (Frame) ThrowError

func (frame Frame) ThrowError(err error)

ThrowError raises err as a Lua error and does not return.

The Go error is preserved as the cause, so errors.Is and errors.As still find it on the *Error a protected caller receives. See Throw.

func (Frame) ThrowString

func (frame Frame) ThrowString(message string)

ThrowString raises a string Lua error and does not return. See Throw.

func (Frame) ToString

func (frame Frame) ToString(value Value) (string, error)

ToString converts value using Lua's tostring semantics from a native callback. The returned string is owned by Go.

func (Frame) UserData

func (frame Frame) UserData(index int) (*UserData, bool)

UserData returns argument index and whether it is exactly Lua userdata.

func (Frame) Where

func (frame Frame) Where(level int) string

Where returns the source position of the activation level levels below this native call, formatted the way Lua positions runtime errors: "chunk.lua:12: ", including the trailing space.

Level 0 is the native call itself and level 1 is the activation that called it, so Where(1) attributes a failure to the call site the way the runtime would. Where returns "" when the requested level has no Lua source position, which covers native activations, eliminated tail calls, and levels past the bottom of the stack.

Raise* and Throw* do not position the messages they are given. Host code that composes its own message and wants runtime-identical attribution prefixes it with Where; ArgError and ArgTypeError leave that choice to the caller for the same reason.

func (Frame) Yield

func (frame Frame) Yield() Outcome

Yield suspends the executing coroutine without yielded values.

The borrowed Frame becomes invalid immediately. Yielding from the main Thread, across another native call, or across a metamethod or iterator boundary produces Lua 5.1's ordinary illegal-yield error instead.

func (Frame) YieldArguments

func (frame Frame) YieldArguments() Outcome

YieldArguments suspends the executing coroutine with every argument passed to this native call. It transfers compact slots directly and does not materialize owning Values.

func (Frame) YieldValue

func (frame Frame) YieldValue(value Value) Outcome

YieldValue suspends the executing coroutine with one owning Value.

func (Frame) YieldValues

func (frame Frame) YieldValues(values ...Value) Outcome

YieldValues suspends the executing coroutine with values.

Values are validated before the execution stack is changed. Unlike a return, yielded values are not adjusted to the caller's requested result count; that adjustment applies later to the arguments supplied at resume.

type Function

type Function hostToken

Function is an opaque owning handle for a Lua or native function.

Repeated publication of the same live Lua function returns the same handle pointer. Execution slots retain the compact function directly and do not pass through this handle.

Function must not be copied after first use. Retain and pass its pointer.

func (*Function) Prototype

func (function *Function) Prototype() *Prototype

Prototype returns function's immutable Prototype.

func (*Function) Value

func (function *Function) Value() Value

Value returns the owning Lua value for function.

type Kind

type Kind uint8

Kind identifies the Lua type held by a Value.

const (
	// InvalidKind identifies the zero Value, which is not a Lua value.
	InvalidKind Kind = iota
	// NilKind identifies Lua nil.
	NilKind
	// BoolKind identifies a Lua boolean.
	BoolKind
	// NumberKind identifies a Lua number.
	NumberKind
	// StringKind identifies a Lua string.
	StringKind
	// FunctionKind identifies a Lua or native function.
	FunctionKind
	// UserDataKind identifies full userdata.
	UserDataKind
	// ThreadKind identifies a Lua thread.
	ThreadKind
	// TableKind identifies a Lua table.
	TableKind
)

func (Kind) String

func (kind Kind) String() string

String returns the Lua type name for kind.

type Library

type Library uint8

Library identifies one Lua standard library.

const (
	BaseLibrary Library = iota
	CoroutineLibrary
	PackageLibrary
	TableLibrary
	IOLibrary
	OSLibrary
	StringLibrary
	MathLibrary
	DebugLibrary
)

type LibrarySet

type LibrarySet []Library

LibrarySet selects the standard libraries installed by New.

Order does not affect installation and duplicate entries are ignored. BaseLibrary includes Lua 5.1's coroutine library; CoroutineLibrary exists so a State can expose coroutines without the base globals.

func CoreLibraries

func CoreLibraries() LibrarySet

CoreLibraries returns the standard libraries that do not themselves grant ambient file, process, environment, or debug access.

The set contains the base (including coroutine), package, table, string, and math libraries. The package library can use preloaded modules without a ScriptLoader and gains no file access on its own.

func FullLibraries

func FullLibraries() LibrarySet

FullLibraries returns every implemented Lua 5.1 standard library.

In addition to CoreLibraries, it includes IO, OS, and debug. Those libraries expose ambient host capabilities and mutable runtime internals.

type NativeFunc

type NativeFunc func(Frame) Outcome

NativeFunc is a Go function callable by Lua.

The Frame is borrowed for the duration of the call. The callback must return an Outcome produced by that Frame. Retaining a Frame or using it after producing a terminal Outcome is a programming error. Go panics are propagated after the borrowed activation is removed; the Raise and argument-error methods are the protected Lua-error paths, while Yield suspends a yieldable coroutine.

type Options

type Options struct {
	// Libraries selects the Lua standard libraries installed by New. Its zero
	// value installs none. CoreLibraries and FullLibraries provide common
	// profiles; a LibrarySet literal selects any other subset.
	Libraries LibrarySet
	// ScriptLoader controls file-backed script loading for State.LoadFile,
	// State.DoFile, Lua loadfile and dofile, and require. Its zero value
	// denies script-file access. Reader- and string-backed loading remain
	// available independently.
	ScriptLoader ScriptLoader
	// Stdin is the State's standard input stream. A nil interface selects
	// os.Stdin. Standard-input consumers share one logical cursor. Lua
	// libraries borrow the stream and never close it. Child processes
	// instead inherit the embedding process's actual os.Stdin unless
	// io.popen connects that descriptor to its returned pipe. Filename-less
	// loadfile and dofile may consume this stream only with HostLoader.
	Stdin io.Reader
	// Stdout is the State's standard output stream. A nil interface selects
	// os.Stdout. Standard-output consumers share one buffering endpoint. Lua
	// libraries borrow the stream and never close it. Child processes
	// instead inherit the embedding process's actual os.Stdout unless
	// io.popen connects that descriptor to its returned pipe.
	Stdout io.Writer
	// Stderr is the State's standard error stream. A nil interface selects
	// os.Stderr. Diagnostic consumers share one buffering endpoint. Lua
	// libraries borrow the stream and never close it. Child processes
	// instead inherit the embedding process's actual os.Stderr.
	Stderr io.Writer
	// Location is the State's local timezone for operating-system library
	// calendar operations. Nil snapshots time.Local when New is called.
	// Later process-global timezone changes do not affect the State.
	Location *time.Location
	// Now supplies wall-clock time to Lua libraries. Nil selects time.Now.
	// The callback runs under the State's single-executor contract and must
	// not reenter that State. If shared by multiple States, it may be called
	// concurrently and must provide its own synchronization.
	Now func() time.Time
	// MaxValues limits values held by ordinary execution. Zero selects 65,536
	// values. Exceeding the limit raises an ordinary Lua error classified as
	// ResourceError. While an xpcall error handler runs, the runtime provides
	// bounded emergency capacity of max(64, MaxValues/8) additional values so
	// the handler can report an exhaustion failure.
	MaxValues int
	// MaxFrames limits ordinary nested Lua and native activations together.
	// Zero selects 20,000 activations. Exceeding the limit raises Lua 5.1's
	// ordinary "stack overflow" error, classified as ResourceError. An xpcall
	// error handler receives bounded emergency capacity of
	// max(8, MaxFrames/8) additional activations.
	MaxFrames int
	// MaxLoadBytes limits bytes consumed while loading one source or binary
	// chunk. Binary decoding independently applies the same bound to projected
	// retained storage. Zero selects 64 MiB. Exceeding either applicable bound
	// returns a ResourceError before the corresponding allocation.
	MaxLoadBytes int
	// MaxHeapBytes limits the logical Lua heap, measured as HeapBytes
	// measures it: Lua objects and their owned storage, not process
	// memory. Opaque userdata payloads and Go allocator overhead are
	// outside the count, so actual process usage is higher. Zero leaves
	// the heap unlimited.
	//
	// The limit is enforced at execution safe points: crossing it schedules
	// a collection, and the runtime raises a ResourceError only if the heap
	// is still over the limit once unreachable objects are gone. A single
	// allocation can therefore overshoot the limit before the runtime
	// observes it, so MaxHeapBytes bounds sustained retention rather than
	// peak allocation. Collection runs more often as retention approaches
	// the limit, so a State held near saturation trades throughput for
	// enforcement.
	//
	// While an xpcall error handler runs, the limit widens by
	// max(64 KiB, MaxHeapBytes/8) so the handler can allocate its report,
	// mirroring the emergency capacity MaxValues and MaxFrames grant.
	//
	// Every operation that runs the executor observes the limit, including
	// host-initiated Lua operations such as Call, SetGlobal, and Index. Raw
	// operations and explicit State.Collect and Frame.Collect do not, so a
	// host can still build and inspect a State that holds more than the
	// limit allows.
	MaxHeapBytes int
}

Options configures a State at construction.

Options is copied by New. Mutating the caller's value after construction does not affect a live State.

type Outcome

type Outcome struct {
	// contains filtered or unexported fields
}

Outcome is the terminal result of a NativeFunc.

Outcomes are bound to the Frame that produced them. The zero value and an Outcome returned from another invocation become Lua runtime failures. A successful Outcome does not retain the executing Thread or State object graph.

type Prototype

type Prototype struct {
	// contains filtered or unexported fields
}

Prototype is immutable, verified Lua executable metadata.

A Prototype is independent of any State and may be shared safely among States. Its executable arrays and constants are private. Strings retained by constants are immutable and do not refer back to a State.

func Compile

func Compile(sourceName, source string) (*Prototype, error)

Compile compiles source as a Lua 5.1 chunk into an immutable, State-neutral Prototype.

The returned Prototype may be shared by multiple States. Compile does not retain source. sourceName is retained for diagnostics and debug information; names beginning with '@' or '=' follow Lua 5.1's file-name and literal-name conventions. Syntax failures are returned as *Error values with category SyntaxError.

func (*Prototype) LineRange

func (prototype *Prototype) LineRange() (first, last int)

LineRange returns the inclusive source line range for prototype.

func (*Prototype) SourceName

func (prototype *Prototype) SourceName() string

SourceName returns the source identifier recorded by the compiler or loader.

type ResultCapacityError

type ResultCapacityError struct {
	Required  int
	Available int
	// contains filtered or unexported fields
}

ResultCapacityError reports that an Into operation produced more results than its destination can hold.

Required is the exact result count produced by Lua. Available is the length of the supplied destination. Lua side effects have already occurred, but the destination remains unchanged. Results returns the completed values.

func (*ResultCapacityError) Error

func (err *ResultCapacityError) Error() string

Error returns a stable description of the insufficient result capacity.

func (*ResultCapacityError) Results

func (err *ResultCapacityError) Results() []Value

Results returns a caller-owned copy of the completed results.

The Values remain valid across later operations and after State.Close.

type ScriptLoader

type ScriptLoader struct {
	// contains filtered or unexported fields
}

ScriptLoader controls how a State opens named Lua scripts.

Its zero value denies script-file access. ScriptLoader values are immutable configuration values: modifier methods return a changed copy.

func FSLoader

func FSLoader(filesystem fs.FS) ScriptLoader

FSLoader loads scripts from filesystem.

Names use fs.FS's slash-separated logical-path contract. The default package.path is "?.lua;?/init.lua". New returns ErrNilScriptFS if filesystem is nil.

Example
scripts := fstest.MapFS{
	"main.lua": {
		Data: []byte(`return require("modules.answer")`),
	},
	"modules/answer.lua": {
		Data: []byte(`return 6 * 7`),
	},
}
state, err := lua.New(lua.Options{
	Libraries:    lua.LibrarySet{lua.PackageLibrary},
	ScriptLoader: lua.FSLoader(scripts),
})
if err != nil {
	panic(err)
}
defer state.Close()
results, err := state.DoFile("main.lua")
if err != nil {
	panic(err)
}
answer, _ := results[0].AsNumber()
fmt.Println(answer)
Output:
42

func FuncLoader

func FuncLoader(opener ScriptOpener) ScriptLoader

FuncLoader loads scripts through opener.

The default package.path is "?.lua;?/init.lua". New returns ErrNilScriptOpener if opener is nil.

func HostLoader

func HostLoader() ScriptLoader

HostLoader loads scripts from the host operating system.

New snapshots LUA_PATH for each State unless WithPackagePath supplies an explicit initial package.path. This is the only loader that also permits filename-less Lua loadfile and dofile to consume Options.Stdin.

func (ScriptLoader) WithPackagePath

func (loader ScriptLoader) WithPackagePath(path string) ScriptLoader

WithPackagePath returns a loader whose initial Lua package.path is path.

The string uses Lua 5.1's semicolon-separated templates. Lua may later replace package.path without changing the State's script backend.

type ScriptOpener

type ScriptOpener func(
	ctx context.Context,
	name string,
) (io.ReadCloser, error)

ScriptOpener opens one logical script name.

Lunar always supplies a non-nil context. The opener should return fs.ErrNotExist when a require search may continue with its next package.path candidate. Lunar closes every non-nil reader returned by the opener, including a reader returned together with an error.

The opener runs under the State's single-executor contract and must not reenter that State. An opener shared by multiple States may be called concurrently and must provide its own synchronization.

type State

type State struct {
	// contains filtered or unexported fields
}

State owns one Lua runtime, its main Thread, and the runtime-wide registry. Each Thread owns its Lua 5.1 global-environment pointer.

A State has one active executor. Callers must serialize all operations on a State; no State method, coroutine Resume, or owned-object mutation may overlap another operation, including Close. Owning Values and object handles may be retained by other goroutines, but their operations remain subject to the same rule.

A State must not be copied after first use. Retain and pass its pointer.

func New

func New(options Options) (*State, error)

New constructs a State and installs the selected standard libraries.

func (*State) Call

func (state *State) Call(
	callable Value,
	arguments ...Value,
) ([]Value, error)

Call invokes callable in protected mode on state's main Thread.

Callable may be a Function or a value with a Function-valued __call metamethod. The returned slice and its Values are owned by the caller and remain valid across later calls and after State.Close. A call with no results returns a nil slice.

Invalid or foreign Values and a State already executing are rejected before Lua runs. Execution failures are returned as *Error. An ExitError unwraps to *ExitRequest and asks the host to apply its own lifecycle policy; Lunar neither closes the State nor terminates the process. A panic from a NativeFunc is propagated after the State has been restored to a callable state.

func (*State) CallDiscard

func (state *State) CallDiscard(
	callable Value,
	arguments ...Value,
) error

CallDiscard invokes callable like Call and discards all results.

func (*State) CallInto

func (state *State) CallInto(
	callable Value,
	arguments []Value,
	destination []Value,
) (count int, err error)

CallInto invokes callable in protected mode on state's main Thread and writes its results into destination.

Arguments are copied before execution, so arguments and destination may overlap. On success, count entries are written and the destination tail is unchanged. On an execution failure or ingress error, destination is unchanged and count is zero. If Lua produces more than len(destination) results, count is the required size and the returned *ResultCapacityError describes the shortfall; destination is still unchanged. Lua side effects completed before that result-count check are not rolled back. Panics from NativeFunc behave as documented by Call.

func (*State) CallOne

func (state *State) CallOne(
	callable Value,
	arguments ...Value,
) (Value, error)

CallOne invokes callable like Call and applies Lua's one-result adjustment.

A call with no results returns Nil. If callable returns several results, only the first is retained.

func (*State) Close

func (state *State) Close() error

Close releases runtime-owned resources and prevents further execution or mutation. Repeated serialized calls are idempotent. Close must not overlap another operation on this State, including another call to Close.

Every still-open runtime-owned native resource is closed exactly once. Borrowed native handles are detached without closing their underlying resources. Before native teardown, Close first drains previously pending userdata __gc handlers, then runs newly eligible handlers in reverse creation order, including handlers on reachable userdata. Lua errors from those handlers are ignored. A panic from a native handler is remembered while later handlers and native cleanup continue, then propagated after the State has closed.

Close continues through every native-resource record and returns cleanup failures joined together, including failures produced by a resource's close-time __gc handler. Buffered standard output is flushed and any flush failures are included in the returned error. The State is closed even when that error is non-nil. Standard streams supplied through Options are borrowed and are never closed.

Previously returned owning Values and canonical object handles remain safe to inspect after Close.

func (*State) Collect

func (state *State) Collect() error

Collect performs one complete semantic collection and runs pending userdata finalizers. Finalizers may execute arbitrary non-yielding Lua. Collect resumes automatic collection after success. It returns a finalizer's Lua error when one occurs; later pending finalizers remain queued for another collection.

Collect requires an idle State. A NativeFunc can use Frame.Collect while Lua is executing.

func (*State) DoFile

func (state *State) DoFile(path string) ([]Value, error)

DoFile opens path through the State's ScriptLoader, then loads and executes a Lua 5.1 source or native binary chunk.

The source name is "@" followed by path. A leading Unix interpreter line is ignored in the same way as Lua 5.1's loadfile. The returned slice and its Values are owned by the caller and remain valid across later calls and after State.Close. DoFile is equivalent to LoadFile followed by Call.

func (*State) DoString

func (state *State) DoString(
	sourceName string,
	source string,
) ([]Value, error)

DoString loads and executes a Lua 5.1 source or native binary chunk.

sourceName is retained for diagnostics and debug information. The returned slice and its Values are owned by the caller and remain valid across later calls and after State.Close. DoString is equivalent to LoadString followed by Call.

func (*State) Equal

func (state *State) Equal(left, right Value) (bool, error)

Equal applies ordinary Lua equality, including __eq where applicable.

func (*State) FunctionEnvironment

func (state *State) FunctionEnvironment(function *Function) (*Table, error)

FunctionEnvironment returns function's Lua 5.1 environment.

func (*State) Global

func (state *State) Global(name string) (Value, error)

Global applies ordinary Lua indexing to the main Thread's global environment.

func (*State) HeapBytes

func (state *State) HeapBytes() (uint64, error)

HeapBytes reports the State's target-architecture logical Lua heap size.

The count covers registered Lua objects and their owned execution and table storage, unique retained string-backing views, and reachable immutable Prototypes. It is not process RSS or Go allocator usage; opaque userdata payloads, host ownership tokens, collector scratch, State infrastructure, and allocator rounding are not attributed to it. HeapBytes scans the live object ledger; it is a measurement operation rather than a cheap per-allocation counter.

func (*State) Index

func (state *State) Index(target, key Value) (Value, error)

Index applies ordinary Lua indexing on the main Thread.

A raw table hit returns directly. Otherwise Index follows __index and may execute Lua. Use Frame.Index from a native callback.

func (*State) Len

func (state *State) Len(value Value) (Value, error)

Len applies Lua's length operator and preserves an arbitrary __len result.

func (*State) Load

func (state *State) Load(
	sourceName string,
	reader io.Reader,
) (*Function, error)

Load reads a Lua 5.1 source or native binary chunk from reader and returns a new Function in the executing Thread's global environment. Load does not execute the resulting chunk and never closes reader.

Reader errors are returned unchanged. Data returned with an error is consumed before that error is reported. Transient reads returning (0, nil) are retried and eventually return io.ErrNoProgress.

func (*State) LoadFile

func (state *State) LoadFile(path string) (*Function, error)

LoadFile opens path through the State's ScriptLoader and loads a Lua 5.1 source or native binary chunk. The source name is "@" followed by path. A leading Unix interpreter line is ignored in the same way as Lua 5.1's loadfile. LoadFile closes the opened reader on every outcome and does not execute the resulting chunk.

func (*State) LoadPrototype

func (state *State) LoadPrototype(
	prototype *Prototype,
) (*Function, error)

LoadPrototype returns a new Lua Function over prototype in the executing Thread's global environment. Outside a callback, that is the main Thread's environment.

Prototype is immutable and State-neutral. Loading the same Prototype in multiple States creates distinct Functions while sharing executable metadata. Root upvalues are initialized to Lua nil, matching Lua 5.1's loader.

func (*State) LoadString

func (state *State) LoadString(
	sourceName string,
	source string,
) (*Function, error)

LoadString loads a Lua 5.1 source or native binary chunk and returns a new Lua Function in the executing Thread's global environment. Outside a callback, that is the main Thread's environment. LoadString does not execute the resulting chunk.

func (*State) MainThread

func (state *State) MainThread() *Thread

MainThread returns the canonical main Thread.

func (*State) Metatable

func (state *State) Metatable(value Value) (*Table, error)

Metatable returns value's metatable without invoking Lua. A nil result means no metatable is installed.

func (*State) NewNativeFunction

func (state *State) NewNativeFunction(
	entry NativeFunc,
) (*Function, error)

NewNativeFunction constructs a canonical native Function.

Its initial environment is the currently executing Function's environment, or the main Thread's global environment outside a callback. State to carry alongside the function belongs in the Go closure; an owning Value held that way keeps its Lua object reachable.

Example
state, err := lua.New(lua.Options{})
if err != nil {
	panic(err)
}
defer func() {
	if err := state.Close(); err != nil {
		panic(err)
	}
}()

add, err := state.NewNativeFunction(func(frame lua.Frame) lua.Outcome {
	left, ok := frame.Number(0)
	if !ok {
		frame.ThrowArgTypeError(0, lua.NumberKind)
	}
	right, ok := frame.Number(1)
	if !ok {
		frame.ThrowArgTypeError(1, lua.NumberKind)
	}
	return frame.ReturnNumber(left + right)
})
if err != nil {
	panic(err)
}
if err := state.SetGlobal("host_add", add.Value()); err != nil {
	panic(err)
}

chunk, err := state.LoadString(
	"@host.lua",
	`return host_add(20, 22)`,
)
if err != nil {
	panic(err)
}
results, err := state.Call(chunk.Value())
if err != nil {
	panic(err)
}
sum, _ := results[0].AsNumber()
fmt.Println(sum)
Output:
42

func (*State) NewTable

func (state *State) NewTable() (*Table, error)

NewTable constructs an empty canonical Table.

func (*State) NewTableFrom

func (state *State) NewTableFrom(tree any) (*Table, error)

NewTableFrom builds a Lua table from a Go value tree in one pass.

It converts nil, bool, the signed and unsigned integer kinds, float32, float64, string, []byte, []any, map[string]any, and an owning Value that already belongs to this State. Nested maps and slices become nested tables; a slice becomes a one-based sequence. Any other Go type reports ErrUnsupportedTreeValue and leaves no partially built table reachable.

Integers wider than float64 can represent lose precision, as they would through any Lua number. Conversion performs raw assignments only: it never invokes __newindex and never executes Lua, so it is also usable from a native callback through Frame.State.

func (*State) NewTableWithCapacity

func (state *State) NewTableWithCapacity(
	arrayHint, recordHint int,
) (*Table, error)

NewTableWithCapacity constructs an empty canonical Table using capacity hints for its array and record parts.

func (*State) NewThread

func (state *State) NewThread(callable Value) (*Thread, error)

NewThread constructs a suspended coroutine whose first resume invokes callable.

Callable may be a Function or a value with a Function-valued __call metamethod. It must belong to state. Construction does not execute Lua. The new Thread inherits the creating Thread's global-environment pointer; later pointer replacement on either Thread is isolated. Lua's coroutine.create is intentionally narrower and accepts only Lua Functions, as required by Lua 5.1.

func (*State) NewUserData

func (state *State) NewUserData(payload any) (*UserData, error)

NewUserData constructs canonical userdata holding payload. Its initial environment is the currently executing Function's environment, or the main Thread's global environment outside a callback.

func (*State) OpenBase

func (state *State) OpenBase() error

OpenBase installs the Lua 5.1 base-library globals.

loadfile and dofile obey the State's ScriptLoader; installing the base library grants no script-file access. Calling OpenBase again replaces every installed function and the coroutine table with fresh canonical objects and restores _G and _VERSION.

func (*State) OpenCoroutine

func (state *State) OpenCoroutine() error

OpenCoroutine installs the Lua 5.1 coroutine library.

Each call replaces the global coroutine table and its functions with fresh canonical objects.

func (*State) OpenDebug

func (state *State) OpenDebug() error

OpenDebug installs the Lua 5.1 debug inspection library.

The library deliberately exposes mutable execution state, raw metatables, and the registry. Applications executing untrusted Lua should not open it. Instruction hooks are not installed because exact hooks would add work to ordinary execution, which Lunar deliberately keeps unchanged. Opening again replaces the debug table and its functions with fresh canonical objects.

func (*State) OpenIO

func (state *State) OpenIO() error

OpenIO installs the Lua 5.1 IO library.

Files are opaque runtime userdata. Standard files borrow the State streams; files returned by open own their operating-system handle. Opening again installs a fresh library, private defaults, functions, and standard userdata while preserving the registry's canonical FILE* metatable. Functions retained from an earlier opening keep their earlier default input and output.

func (*State) OpenMath

func (state *State) OpenMath() error

OpenMath installs the Lua 5.1 math library.

Each call replaces the global math table, its functions, and its private random generator with fresh canonical objects.

func (*State) OpenOS

func (state *State) OpenOS() error

OpenOS installs Lua 5.1's operating-system library.

Each call replaces the global os table and every installed function with fresh canonical objects.

func (*State) OpenPackage

func (state *State) OpenPackage() error

OpenPackage installs Lua 5.1's package table plus the global require and module functions.

Lua modules load through the State's ScriptLoader and the same bounded source and binary pipeline as LoadFile. package.loaders contains the preload and Lua-source searchers. Native C modules are deliberately unavailable in this pure-Go runtime, so package.cpath is empty and package.loadlib reports that dynamic libraries are unavailable.

Each call installs fresh package, loader, and Function objects while preserving the State-owned package.preload table, the registry-backed package.loaded table, and its cached modules.

func (*State) OpenString

func (state *State) OpenString() error

OpenString installs the Lua 5.1 string library and the shared string metatable.

Each call replaces the global string table, its functions, and the metatable every Lua string indexes through, so ("x"):upper() resolves to the freshly installed library.

Positions follow Lua 5.1 exactly: they are one-based, a negative position counts back from the end, and out-of-range positions clamp rather than fail. Every operation is byte-oriented; nothing here interprets UTF-8, and the character classes are C's in the "C" locale.

func (*State) OpenTable

func (state *State) OpenTable() error

OpenTable installs the Lua 5.1 table library.

Each call replaces the global table library and its functions with fresh canonical objects.

Every entry operates on raw storage, as Lua 5.1 does: element access uses raw integer reads and writes, and the sequence length is the same border the length operator reports. Only an explicit callback, a comparator, or an __lt handler can run Lua.

func (*State) PreloadModule

func (state *State) PreloadModule(
	name string,
	loader NativeFunc,
) error

PreloadModule registers a native loader in the State-owned package.preload table.

Registration works before or after OpenPackage. Every OpenPackage call publishes the same preload table, so registrations survive reopening. Require still caches successful loads in package.loaded.

The loader follows NewNativeFunction's validation and environment rules. The module name is interpreted like Lua 5.1 require and therefore ends at its first NUL byte.

func (*State) RawEqual

func (state *State) RawEqual(left, right Value) (bool, error)

RawEqual applies Lua raw equality without invoking metamethods.

func (*State) RawGlobal

func (state *State) RawGlobal(name string) (Value, error)

RawGlobal returns a raw value from the current global environment.

During a native callback, current means the executing Thread. Otherwise it means the main Thread.

func (*State) RawSetGlobal

func (state *State) RawSetGlobal(name string, value Value) error

RawSetGlobal performs a raw assignment in the current global environment.

During a native callback, current means the executing Thread. Otherwise it means the main Thread.

func (*State) Registry

func (state *State) Registry() (*Table, error)

Registry returns the private Lua registry table.

The returned table is canonical. It is not an execution stack or a set of pseudo-indexed Go registers.

func (*State) RemoveContext

func (state *State) RemoveContext() error

RemoveContext clears the installed context. Execution already stopped by cancellation is not resumed.

func (*State) RestartGC

func (state *State) RestartGC() error

RestartGC resumes automatic collection and requests a cycle, which the runtime services at the next execution safe point.

func (*State) SetContext

func (state *State) SetContext(ctx context.Context) error

SetContext installs the context the runtime observes while Lua executes.

The context is ambient: it outlives one call and applies to every thread of the State until SetContext replaces it or RemoveContext clears it. Cancellation stops execution and surfaces as a *Error in the ContextError category, so Lua pcall cannot catch it and a script cannot outlast it.

The runtime observes cancellation at bounded safe points between instructions and around native calls, never inside one, so cancellation cannot preempt host code that is already running; a long-running callback observes its own Context. Loading also polls while reading, compiling, and decoding.

SetContext takes effect immediately, including when a native callback installs a new deadline during the call it is running under. It is a State operation and must be serialized like any other; a host deciding to cancel from another goroutine does so through its own context.

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
state.SetContext(ctx)
defer state.RemoveContext()
results, err := state.Call(handler)

A context left installed applies to later operations. A host that cancels per operation clears it when that operation completes.

func (*State) SetFunctionEnvironment

func (state *State) SetFunctionEnvironment(function *Function, environment *Table) error

SetFunctionEnvironment replaces function's Lua 5.1 environment.

func (*State) SetFunctions

func (state *State) SetFunctions(
	table *Table,
	functions map[string]NativeFunc,
) error

SetFunctions installs native functions into table.

SetFunctions validates the State, table, and every function before changing table, so a validation failure never leaves a partial installation. Existing fields with matching names are replaced.

func (*State) SetGlobal

func (state *State) SetGlobal(name string, value Value) error

SetGlobal applies an ordinary Lua assignment to the main Thread's global environment.

func (*State) SetIndex

func (state *State) SetIndex(target, key, value Value) error

SetIndex applies an ordinary Lua assignment on the main Thread.

An existing table field is replaced directly. Otherwise SetIndex follows __newindex and may execute Lua. Use Frame.SetIndex from a native callback.

func (*State) SetMetatable

func (state *State) SetMetatable(value Value, metatable *Table) error

SetMetatable replaces value's metatable without invoking Lua. Passing nil removes the metatable.

func (*State) StopGC

func (state *State) StopGC() error

StopGC suspends automatic collection. Explicit Collect still runs, and so does a collection requested by Lua's collectgarbage.

Retention is unbounded while the collector is stopped. A State with Options.MaxHeapBytes still measures its heap at execution safe points, so stopping the collector can surface the limit that automatic collection would otherwise have avoided.

func (*State) String

func (state *State) String(text string) Value

String returns an owning Lua string Value.

Strings are immutable and State-neutral. A returned Value may be shared among States and remains safe after this State is closed. Calling String after Close returns is permitted and constructs an uncached Value. As with every State operation, String must not overlap Close on the same State.

func (*State) ToString

func (state *State) ToString(value Value) (string, error)

ToString converts value using Lua's tostring semantics.

ToString honors __tostring and requires that metamethod to return a string. It differs from Value.String, which is diagnostic and never executes Lua.

type Table

type Table hostToken

Table is an opaque owning handle for a Lua table.

Table methods are raw: they never invoke Lua or consult metamethods. Metamethod-aware operations belong to State and Frame.

Repeated publication of the same live Lua object returns the same handle pointer. Execution slots retain the compact table directly and do not pass through this handle.

Table must not be copied after first use. Retain and pass its pointer.

func (*Table) Next

func (table *Table) Next(
	after Value,
) (key, value Value, ok bool, err error)

Next returns the table field after after in Lua's raw traversal order.

Pass Nil() to begin a traversal, then pass each returned key to the next call. If no field remains, ok is false and key and value are Nil. Next does not invoke metamethods.

Deleting the current field or changing an existing field's value is permitted between calls. Adding a new field during traversal makes the traversal order and visited set undefined. A continuation key that Lua cannot locate returns ErrInvalidNextKey.

Like the other raw readers, Next observes a closed State's tables as the frozen snapshot State.Close leaves behind.

func (*Table) RawGet

func (table *Table) RawGet(key Value) (Value, error)

RawGet returns the value associated with key without invoking metamethods. A missing key returns Nil.

func (*Table) RawGetInt

func (table *Table) RawGetInt(key int) Value

RawGetInt returns the value associated with an integer key without invoking metamethods. A missing key returns Nil.

func (*Table) RawGetString

func (table *Table) RawGetString(key string) Value

RawGetString returns the value associated with a string key without constructing a temporary Value or invoking metamethods.

func (*Table) RawLen

func (table *Table) RawLen() int

RawLen returns a valid Lua border for table without invoking __len.

As in Lua 5.1, the result is undefined when a table has more than one border.

func (*Table) RawSet

func (table *Table) RawSet(key, value Value) error

RawSet associates key with value without invoking metamethods. Assigning Nil deletes the key.

func (*Table) RawSetInt

func (table *Table) RawSetInt(key int, value Value) error

RawSetInt associates an integer key with value without invoking metamethods.

func (*Table) RawSetString

func (table *Table) RawSetString(key string, value Value) error

RawSetString associates a string key with value without invoking metamethods.

func (*Table) Value

func (table *Table) Value() Value

Value returns the owning Lua value for table.

type Thread

type Thread hostToken

Thread is an opaque owning handle for a Lua thread.

Repeated publication of the same live Lua thread returns the same handle pointer. Execution slots retain the compact thread directly and do not pass through this handle. Resume operations must be serialized with every other operation on the owning State.

A Thread must not be copied after first use. Retain and pass its pointer.

func (*Thread) IsMain

func (thread *Thread) IsMain() bool

IsMain reports whether thread is its State's main thread.

func (*Thread) Resume

func (thread *Thread) Resume(
	arguments ...Value,
) (results []Value, status ThreadStatus, err error)

Resume starts or continues a suspended coroutine.

On a yield, status is ThreadSuspended and results are the yielded values. On a final return, status is ThreadDead and results are the function's return values. An execution failure also leaves the coroutine dead and is returned as *Error. ExitError asks the embedding host to apply its own lifecycle policy and is never converted to an ordinary coroutine result. The returned slice and Values are owned by the caller.

func (*Thread) ResumeInto

func (thread *Thread) ResumeInto(
	arguments []Value,
	destination []Value,
) (count int, status ThreadStatus, err error)

ResumeInto starts or continues a suspended coroutine and writes its yielded or returned values into destination.

Arguments are copied before execution, so arguments and destination may overlap. If destination is too short, count reports the required size and the coroutine has already yielded or returned, but destination is unchanged. The call may be retried only by resuming from the coroutine's new status, not by repeating the completed transition.

func (*Thread) State

func (thread *Thread) State() *State

State returns the State that owns thread.

func (*Thread) Status

func (thread *Thread) Status() ThreadStatus

Status returns thread's current status.

func (*Thread) Value

func (thread *Thread) Value() Value

Value returns the owning Lua value for thread.

type ThreadStatus

type ThreadStatus uint8

ThreadStatus describes a Thread's execution state.

const (
	// ThreadReady identifies a Thread that has not started or is idle.
	ThreadReady ThreadStatus = iota
	// ThreadRunning identifies the currently executing Thread.
	ThreadRunning
	// ThreadNormal identifies a coroutine waiting for a coroutine it resumed.
	ThreadNormal
	// ThreadSuspended identifies a coroutine stopped at yield.
	ThreadSuspended
	// ThreadDead identifies a coroutine that returned or failed.
	ThreadDead
	// ThreadClosed identifies a Thread whose State has closed.
	ThreadClosed
)

type TraceFrame

type TraceFrame struct {
	// Source is the source identifier recorded by the Prototype.
	Source string
	// Function is the best available Lua function name.
	Function string
	// Line is the one-based source line, or zero when unavailable.
	Line int
	// TailCalls is the number of frames eliminated immediately below this
	// surviving frame by proper tail calls.
	TailCalls uint32
}

TraceFrame is an immutable source-level traceback entry.

func (TraceFrame) String

func (entry TraceFrame) String() string

String renders one traceback entry the way Lua positions a frame: "chunk.lua:12: in function 'name'". It never executes Lua and stays valid after the owning State closes.

type UserData

type UserData hostToken

UserData is an opaque owning handle for a Lua userdata object holding a Go value.

The payload is opaque to Lua unless native functions expose operations on it. Metatable and environment changes are controlled by State operations. Repeated publication of the same live Lua object returns the same handle pointer. Execution slots retain the compact object directly and do not pass through this handle.

UserData must not be copied after first use. Retain and pass its pointer.

func (*UserData) Data

func (data *UserData) Data() any

Data returns the Go payload. Reading the payload remains safe after the owning State closes. Userdata reserved for a runtime library has no public payload and returns nil.

func (*UserData) SetData

func (data *UserData) SetData(payload any) error

SetData replaces the Go payload. Runtime-owned userdata returns ErrReadOnlyUserData.

func (*UserData) Value

func (data *UserData) Value() Value

Value returns the owning Lua value for userdata.

type UserDataType

type UserDataType[T any] struct {
	// contains filtered or unexported fields
}

UserDataType is a State-bound descriptor for one class of Go-backed Lua userdata.

A value matches the descriptor only when it belongs to the descriptor's State, has the descriptor's exact metatable, and its payload is assignable to T. The descriptor's metadata and typed reads remain available after the State closes; constructing new userdata does not.

func NewUserDataType

func NewUserDataType[T any](
	state *State,
	name string,
) (*UserDataType[T], error)

NewUserDataType returns the canonical State-local userdata type named name.

Repeating the same name and T reuses its metatable. Reusing name with a different T returns ErrUserDataTypeConflict. Registrations are held in a private State registry that is not exposed through debug.getregistry.

Example
state, err := lua.New(lua.Options{})
if err != nil {
	panic(err)
}
defer state.Close()

counterType, err := lua.NewUserDataType[*exampleCounter](
	state,
	"example.Counter",
)
if err != nil {
	panic(err)
}
methods, err := state.NewTable()
if err != nil {
	panic(err)
}
if err := state.SetFunctions(
	methods,
	map[string]lua.NativeFunc{
		"add": func(frame lua.Frame) lua.Outcome {
			counter, ok := counterType.FromArgument(frame, 0)
			if !ok {
				frame.ThrowArgError(
					0,
					counterType.Name()+" expected",
				)
			}
			amount := int64(1)
			if !frame.IsMissingOrNil(1) {
				amount, ok = frame.IntegerInRange(
					1,
					-1_000,
					1_000,
				)
				if !ok {
					frame.ThrowArgError(
						1,
						"bounded integer expected",
					)
				}
			}
			counter.value += amount
			return frame.ReturnNumber(float64(counter.value))
		},
	},
); err != nil {
	panic(err)
}
if err := counterType.Metatable().RawSetString(
	"__index",
	methods.Value(),
); err != nil {
	panic(err)
}

newCounter, err := state.NewNativeFunction(
	func(frame lua.Frame) lua.Outcome {
		initial, ok := frame.Integer(0)
		if !ok {
			frame.ThrowArgTypeError(0, lua.NumberKind)
		}
		counter, createErr := counterType.New(
			&exampleCounter{value: initial},
		)
		if createErr != nil {
			frame.ThrowError(createErr)
		}
		return frame.ReturnValue(counter.Value())
	},
)
if err != nil {
	panic(err)
}
if err := state.SetGlobal("new_counter", newCounter.Value()); err != nil {
	panic(err)
}

results, err := state.DoString("@counter.lua", `
local counter = new_counter(10)
return counter:add(5), counter:add()
`)
if err != nil {
	panic(err)
}
first, _ := results[0].AsNumber()
second, _ := results[1].AsNumber()
fmt.Println(first, second)
Output:
15 16

func (*UserDataType[T]) FromArgument

func (descriptor *UserDataType[T]) FromArgument(
	frame Frame,
	index int,
) (T, bool)

FromArgument returns the typed payload when Frame argument index belongs to this exact userdata type.

Argument indexes are zero-based. A missing argument or mismatched Lua class or Go payload returns false.

func (*UserDataType[T]) FromValue

func (descriptor *UserDataType[T]) FromValue(value Value) (T, bool)

FromValue returns the typed payload when value belongs to this exact userdata type.

It returns false for another State, Lua kind, userdata metatable, or Go payload type. Reading an owning value remains safe after the State closes.

func (*UserDataType[T]) Metatable

func (descriptor *UserDataType[T]) Metatable() *Table

Metatable returns the canonical metatable for this userdata type. A zero descriptor returns nil.

func (*UserDataType[T]) Name

func (descriptor *UserDataType[T]) Name() string

Name returns the State-local registration name. A zero descriptor returns an empty string.

func (*UserDataType[T]) New

func (descriptor *UserDataType[T]) New(payload T) (*UserData, error)

New constructs userdata holding payload and installs the descriptor's exact metatable.

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value is an owning Lua value.

Its fields are private so executable state cannot be mutated through Go. Copying a Value is cheap, does not allocate, and keeps referenced Go memory visible to the garbage collector. The zero Value is invalid; use Nil() for Lua nil.

Value is deliberately not comparable. Use State.RawEqual for Lua raw equality or SameObject when reference identity is specifically required.

func Bool

func Bool(value bool) Value

Bool returns the Lua boolean corresponding to value.

func Nil

func Nil() Value

Nil returns the Lua nil value.

func Number

func Number(value float64) Value

Number returns a Lua number.

func String

func String(text string) Value

String returns a State-neutral Lua string.

The returned Value may be shared among States and remains valid after any State is closed. State.String may reuse a State's short-string cache when constructing strings repeatedly.

func (Value) AsBool

func (value Value) AsBool() (bool, bool)

AsBool returns the contained boolean and whether value is a boolean.

func (Value) AsFunction

func (value Value) AsFunction() (*Function, bool)

AsFunction returns the canonical function and whether value is a function.

func (Value) AsNumber

func (value Value) AsNumber() (float64, bool)

AsNumber returns the contained number and whether value is a number.

func (Value) AsString

func (value Value) AsString() (string, bool)

AsString returns the contained string and whether value is a string.

func (Value) AsTable

func (value Value) AsTable() (*Table, bool)

AsTable returns the canonical table and whether value is a table.

func (Value) AsThread

func (value Value) AsThread() (*Thread, bool)

AsThread returns the canonical thread and whether value is a thread.

func (Value) AsUserData

func (value Value) AsUserData() (*UserData, bool)

AsUserData returns the canonical userdata and whether value is userdata.

func (Value) IsNil

func (value Value) IsNil() bool

IsNil reports whether value is Lua nil.

func (Value) Kind

func (value Value) Kind() Kind

Kind returns the Lua type held by value. It returns InvalidKind for the zero Value.

func (Value) SameObject

func (value Value) SameObject(other Value) (same, applicable bool)

SameObject reports reference identity.

applicable is true only for tables, functions, userdata, and threads. Strings compare by contents under Lua semantics and therefore are not reference objects for this operation.

func (Value) String

func (value Value) String() string

String returns a stable diagnostic representation without executing Lua. Numbers use Lua 5.1's `%.14g`-style spelling, which is not a lossless serialization format.

func (Value) Truth

func (value Value) Truth() bool

Truth reports Lua truthiness. Only nil and false are false.

func (Value) Valid

func (value Value) Valid() bool

Valid reports whether value contains a Lua value.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL