Help us shape the future of Go! Take the 2026 Go Developer Survey

lua

package module
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 27 Imported by: 0

README

gopher-lua (pschlump fork) — Lua 5.5.1 in pure Go

A Lua 5.5.1 virtual machine, compiler, and standard library in pure Go — byte-exact with stock C Lua 5.5.1 (error messages, semantics, numeral formatting, observable table-iteration order), no cgo, built as the embedded scripting engine for Ultima, a pure-Go Redis clone (EVAL/SCRIPT). It runs as a single static Go binary on every platform Go supports.

Credit where credit is due

This repo is a fork of gopher-lua by Yusuke Inuzuka — a capable Lua VM and compiler in Go (implementing Lua 5.1). The parser, the register-VM interpreter, the code generator, and the Go embedding API all originate there. Without gopher-lua, this fork would not exist. Thank you, yuin.

The fork lineage runs through glasm, which upgraded the language core in place from 5.2 through Lua 5.5.1 semantics and gated it byte-exact against stock C Lua 5.5.1; this repo was carved from glasm (0affa95, 2026-11-01) as the standalone native engine. It builds on that foundation with:

  • the full Lua 5.5.1 language surface — stock 5.5.1 instruction encoding, _ENV, integer/float duality, goto, <const>/<close>, the 5.3+ libraries — differentially gated against stock C Lua 5.5.1 (58-case golden corpus, byte-exact output);
  • the daemon-facing host/ package: untrusted scripts with memory budgets, deadlines and kill, sandboxed globals, Go host functions, and a warm-VM pool — the Redis-EVAL embedding posture (below);
  • a measured performance program (table engine, call path, boxing) — see How about performance?.

Table of Contents

Design principle

  • Be a scripting language with extensible semantics, with a user-friendly Go API. The API is object-based, not the stack-based API of the C implementation.
  • Byte-exactness with stock C Lua 5.5.1 is the contract. Stock is the spec: error strings, tostring formatting, iteration order — any divergence is a ledgered row with a covering test, not an accident.
  • Pure Go, no cgo. The engine embeds in any Go daemon (Ultima's EVAL, an HTTP service) as plain library code: static binaries, per-VM memory budgets, and per-instruction preemption — none of which a cgo-bound C Lua can give a Go host without a process boundary.
  • LValue is an interface{} — the public API. Consumers pattern-match on it (lv.(*LFunction), v.(LInteger)); embedding friendliness outranks the last few percent of interpreter speed.

How about performance?

All numbers below are from one Apple M4 Max (arm64, go1.27.0) in one 2026-11-02 session, tree at the P6 perf milestone; the C reference is stock C Lua 5.5.1 built -O2 (clang 21). Full methodology and raw runs: docs/Perf-P0-Baseline.md (the same-session C calibration), docs/Perf-P6-Boxing.md (engine numbers), and docs/Embedded-EVAL-Perf.md (the embedded suite). The meaningful column is always the ratio — absolute numbers move with the machine. (cmd/luabench and cmd/embedcmp reproduce any of it.)

vs the native C Lua 5.5.1 interpreter

Benchmark scripts (cmd/luabench), warm steady state. The C leg is the lua CLI re-loadfileing per run — the best its CLI can do, since it has no pooling surface; the gopher-lua leg is the daemon posture (pooled VM, live cancelable context):

script (shape) C Lua 5.5.1 gopher-lua ×C
h_tiny (startup only) 14.0 µs 1.8 µs 0.13×
h_concat (concat-heavy) 620 µs 470 µs 0.76×
h_str (short string ops) 36 µs 57 µs 1.6×
h_tablechurn (table alloc churn) 3.9 ms 7.4 ms 1.9×
h_strlong (150 KB text processing) 387 µs 1.0 ms 2.6×
h_tco (tail-recursive countdown) 1.6 ms 8.9 ms 5.5×
h_fib(30) (call-heavy recursion) 20.2 ms 119 ms 5.9×
h_callret (call-heavy) 2.4 ms 15.0 ms 6.2×
h_nsieve (numeric + table stores) 6.0 ms 58 ms 9.7×
h_numloop (4 M-iteration numeric) 8.5 ms 99 ms 11.7×

The shape is the story. Where the work is setup-shaped (compile a small script, run it, return) a pooled gopher-lua VM beats the C CLI — it re-runs an already-compiled chunk while C re-parses. Where the work is per-instruction compute, -O2 C's dispatch wins: string and table lanes within ~2×, call-shaped lanes ~6×, tight numeric boxing-heavy loops ~12× (a 5.1→11.7× improvement across this fork's perf program; numloop was 22.2×C at its baseline).

Embedded usage — the EVAL shape (cmd/embedcmp)

The table above flatters C: a daemon does not shell out to lua. The fair comparison embeds C Lua through its own C API the way Redis does — one lua_State, chunk loaded once, fresh KEYS/ARGV staged per call — and races it against host/ over the same scripts with byte-identical staged arguments and cross-checked results (zero mismatches across the suite):

script (short EVAL-shape) C, state reuse C, fresh state gopher-lua, fresh VM gopher-lua, pooled VM pooled ×C
e_arg (arg arithmetic) 0.4 µs 15.4 µs 13.8 µs 2.7 µs 7.2×
e_sum (KEYS aggregation) 0.8 µs 17.0 µs 15.8 µs 4.6 µs 6.1×
e_tag (reply array build) 1.3 µs 17.3 µs 16.4 µs 5.4 µs 4.0×
e_norm (string normalize) 0.5 µs 16.6 µs 18.2 µs 7.2 µs 15.8×
e_hash (40-field hash loop) 5.4 µs 23.5 µs 22.5 µs 11.9 µs 2.2×
e_call (200 Go-fn crossings) 14.8 µs 38.7 µs 52.6 µs 44.3 µs 3.0×
e_sort (500-element sort) 44.7 µs 58.8 µs 142.0 µs 132.6 µs 3.0×

Reading it:

  • Against the state-reuse embedder, scripts that do real work run at 2–3× C; the residual on trivial scripts is a fixed ~2.6 µs per-run floor (pool dispatch + KEYS/ARGV staging + result collection) against C's ~0.2 µs, not per-instruction slowness.
  • A host-function crossing costs ~0.21 µs (vs C's ~72 ns through its C API): a Go closure, argument conversion, no wire.
  • Fresh-VM-per-run — the maximum-isolation posture, no shared guest state at all — lands at 13.8–18.2 µs on trivial scripts, on par with C's own newstate-per-run (and a fresh VM here is a µs-scale LState build, not a process).
  • First run after daemon start: 33–186 µs in-process; compile is 32–41 µs vs C's 8–10 µs loadbuffer (the known parser gap — it is a real parse, no cache tier, paid once per script).
Memory utilization

Peak process RSS over the same embedded suite (one Lua state / VM in the loop):

posture C Lua 5.5.1 gopher-lua
one-shot process 2.0–2.1 MB 7.2–7.6 MB
warm loop 2.1–2.5 MB 8.2–8.8 MB
live heap after loop ~20 KB Lua heap ~0.5 MB Go heap

The ~4× RSS is the Go runtime floor plus one LState and the standard library tables — it is a process number, not a per-VM multiplier: the runtime is shared, and each additional VM costs its LState + library tables (sub-megabyte). What the 4× buys a daemon:

  • no cgo — one static binary, every platform Go targets, no C allocator arenas next to the Go heap;
  • per-VM memory budgets — WithMemoryBudgetBytes charges every allocation seam (table growth, string builders) against a monotone accounted counter and raises a catchable not enough memory past the cap; vm.UsedBytes() reports the charge;
  • per-instruction preemption — a deadline or Kill() stops the script at the very next instruction boundary (a C interpreter can only be made cooperative, never preempted, from a Go host);
  • coroutines are goroutines, with a per-VM cap.

C Lua is the right choice for a C daemon. For a Go daemon, the comparison is not "8.8 MB vs 2.3 MB" but "8.8 MB in-process with budgets and kill" vs "a subprocess C Lua per script (or a cgo seam with none of it)".

The perf program

These numbers are the product of a measured, milestone-gated program (P0–P6, docs/Option-A-Plan.md) — context checks, the table engine, the call path, string-constant hashing, the concat fold, and boxing — compounding to ≈ −35% geomean against its own baseline, every step benchstat-gated and byte-exactness-gated.

Installation

go get github.com/pschlump/gopher-lua@v0.0.8

Usage

import (
    lua "github.com/pschlump/gopher-lua"
)

L := lua.NewState()
defer L.Close()
if err := L.DoString(`print(("Lua"):upper() .. " " .. _VERSION)`); err != nil {
    panic(err)
}
L := lua.NewState()
defer L.Close()
if err := L.DoFile("hello.lua"); err != nil {
    panic(err)
}

Refer to the Lua 5.5 Reference Manual and the Go doc for LState methods. The API is the gopher-lua API (objects, not stack indices) wherever the 5.5 upgrade kept it; see the sections below and docs/.

Data model

All data in a Lua program is an LValue — an interface with String() string and Type() LValueType:

Type name Go type Type() value Constants
LNilType (constants) LTNil LNil
LBool (constants) LTBool LTrue, LFalse
LNumber float64 LTNumber -
LInteger int64 LTInteger -
LString string LTString -
LFunction struct pointer LTFunction -
LUserData struct pointer LTUserData -
LState struct pointer LTThread -
LTable struct pointer LTTable -
LChannel chan LValue LTChannel -

Test types the Go way (type assertion) or via Type(). LBool, LNumber, LInteger, LString are values, not pointers; test nil/booleans against the constants (lv == lua.LTrue). lua.LVIsFalse / lua.LVAsBool implement Lua truthiness (nil and false are false).

Calling Go from Lua
func Double(L *lua.LState) int {
    lv := L.ToInt(1)              /* get argument */
    L.Push(lua.LNumber(lv * 2))   /* push result */
    return 1                      /* number of results */
}

L.SetGlobal("double", L.NewFunction(Double))
print(double(20)) -- > "40"

Coroutines, user-defined types (LUserData + metatables), module creation (PreloadModule), and calling Lua from Go (CallByParam) all work as in upstream gopher-lua — see the Go doc and the upstream README; the 5.5 upgrade changed none of those shapes.

Callstack & Registry size

An LState's registry and callstack sizes are tunable per state — worth doing when many LStates live in one process:

L := lua.NewState(lua.Options{
    RegistrySize:     1024 * 20, // initial registry size
    RegistryMaxSize:  1024 * 80, // max growth (0 = fixed)
    RegistryGrowStep: 32,
    CallStackSize:    120,
    // MinimizeStackMemory: true, // page callframes in/out (default: fixed)
    // SkipOpenLibs:        true, // open libs yourself (sandboxing)
})
Terminating a running LState

The interpreter checks a Go context at every instruction — a cancel or deadline raises an ordinary (pcall-catchable) error at the next instruction boundary:

L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
L.SetContext(ctx)
err := L.DoString(`
  local clock = os.clock
  function sleep(n)  -- busy-wait seconds
    local t0 = clock()
    while clock() - t0 <= n do end
  end
  sleep(3)
`)
// err.Error() contains "context deadline exceeded"

Embedding in a server: the host/ package

host/ is the daemon surface over the interpreter — what Ultima's EVAL/SCRIPT runs on. One process-wide Engine holds the script cache and the host-function registry; each Run executes a compiled script against a VM with fresh KEYS/ARGV staging, a deadline, and a memory budget. Host functions are plain Go closures (the redis.call mechanism). Scripts are untrusted by default: safe stdlibs, locked globals, capped coroutines, accounted memory.

A complete Go HTTP server — script endpoint, host functions, sandbox, and script timeouts:

package main

import (
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/pschlump/gopher-lua/host"
)

var engine *host.Engine

func main() {
	engine = host.NewEngine(
		host.WithVMCacheSize(16),          // pool warm VMs per script (repeat scripts skip VM build)
		host.WithCompileMemCache(1024),    // bound the script cache (LRU)
		host.WithGlobalsProtection(true),  // Redis-classic lockdown: unknown globals raise, _G readonly
		host.WithMemoryBudgetBytes(32<<20),// per-VM accounted budget: 32 MB, catchable overrun
		host.WithMaxCoroutines(64),        // coroutines are goroutines — cap them
	)

	// Host functions: Go closures the script calls. A closure runs on the
	// VM's goroutine and must not call vm.Run (reentrancy is refused).
	engine.RegisterGlobal("app", "get", func(vm *host.VM, args []host.Value) ([]host.Value, error) {
		key := args[0].Str // arguments arrive as owning seam Values
		// ...fetch from your store...
		return []host.Value{host.String("value-of-" + key)}, nil
	})
	engine.RegisterGlobal("app", "incr", func(vm *host.VM, args []host.Value) ([]host.Value, error) {
		return []host.Value{host.Int(args[0].Int + 1)}, nil
	})
	// Constants: redis.LOG_WARNING-shaped values.
	engine.RegisterValue("app", "VERSION", host.String("1.2.3"))

	http.HandleFunc("/script", scriptHandler)
	http.ListenAndServe(":8080", nil)
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	src := r.URL.Query().Get("src") // e.g.:
	//   local v = app.get(KEYS[1]) return app.incr(ARGV[1] + 0), app.VERSION
	script, err := engine.Compile([]byte(src), "=client") // syntax errors verbatim
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// The run: fresh KEYS/ARGV tables, bounded by a hard 5 s deadline.
	res, err := engine.Run(r.Context(), script, host.RunOptions{
		Keys:     []string{"user:1", "user:2"},
		Argv:     []string{"41"},
		Deadline: 5 * time.Second,
		Seed:     0, // optional: math.randomseed(n) per run
	})
	if err != nil {
		var se *host.ScriptError
		switch {
		case errors.As(err, &se) && se.ErrValue.String() == "context deadline exceeded":
			// The TIMEOUT fired (or the request context was canceled):
			// the interpreter's per-instruction check raised at the first
			// instruction past the deadline — bare, Line 0.
			http.Error(w, "script timed out", http.StatusRequestTimeout)
		case errors.As(err, &se):
			// A script-level error (raise, library, host fn). se.Line is
			// the raise line — the "on @user_script:N" position.
			http.Error(w, fmt.Sprintf("script error (line %d): %s", se.Line, se.ErrValue.String()), http.StatusBadRequest)
		default:
			// Engine-level failure (a bug class, not a script error).
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}
	for _, v := range res.Values {
		fmt.Fprintln(w, v.String())
	}
}

Timeouts, precisely. RunOptions.Deadline wraps the run's context in a timer; the interpreter checks it at every instruction (and at every Go-function return), so even a tight numeric loop stops within one instruction of the deadline:

script, _ := engine.Compile([]byte(`
	local i = 0
	while true do i = i + 1 end	-- would run forever
`), "=spin")

_, err := engine.Run(context.Background(), script,
	host.RunOptions{Deadline: 100 * time.Millisecond})
// err is *host.ScriptError{ErrValue: host.String("context deadline exceeded"), Line: 0}

The same normalization covers a caller-context cancel and vm.Kill() — the SCRIPT KILL mechanism, callable from another goroutine while the run holds the VM. Deadlines are not free (the check is amortized to every 64th instruction; the warm lanes above run with a live cancelable context) but they are always on in the daemon posture.

Concurrency law: a VM serves one run at a time (its own mutex); any number of VMs run in parallel. engine.Run makes that automatic — a pooled VM is taken atomically, a pool miss builds a fresh VM. Guest errors keep the VM pooled (the Lua stack unwinds to base); engine failures close it; a VM that has served 1024 runs is recycled.

The low-level alternative — lua.NewState + L.SetContext + DoString, with your own sync.Pool — remains fully supported (see "Terminating a running LState" above); host/ exists because the deadline/kill/budget/pool plumbing is exactly what every daemon ends up rewriting.

Differences between Lua and gopher-lua

  • os.setlocale, package.loadlib are not implemented; lua_Debug.namewhat is not populated.
  • collectgarbage takes no arguments and runs the collector for the whole Go program.
  • file:setvbuf has no line buffering; DST is not supported.
  • Channels (channel.select, LChannel) are a Go-native extension; coroutines are goroutines.
  • In production, host/ scripts get the safe stdlib set (no io/os/package/debug); require, loadfile, dofile, load, warn are nil'd even when the base library opens.

Standalone interpreter

go install github.com/pschlump/gopher-lua/cmd/glua@v0.0.8
glua script.lua   # no args: REPL

Build & test

make build   # regenerates state.go/vm.go from _state.go/_vm.go (never edit the generated files) + builds cmd/glua
make test    # regenerate + go test ./...
go test -short ./...   # skip the 26 s official-suite golden leg

The golden-corpus gate (_goldens/, 58 cases over the official 5.5 suite + fork cases) is the byte-exactness contract's enforcement: GOLDEN_UPDATE=1 go test . -run TestGolden regenerates, and every diff line must cite a ledger row in docs/Divergence-Ledger.md.

License

MIT — see LICENSE. Upstream copyright Yusuke Inuzuka (yuin/gopher-lua).

Authors

Yusuke Inuzuka (original gopher-lua) · Philip Schlump

Documentation

Overview

gopher-lua (pschlump fork): a Lua 5.5.1 virtual machine and compiler in pure Go — the embedded scripting engine for the Ultima project. Carved from glasm @ 0affa95 (2026-11-01); both are forks of yuin/gopher-lua (upstream copyright and license in LICENSE).

Index

Constants

View Source
const (
	VarArgHidden uint8 = 1 // PF_VAHID — hidden extra args (stock default)
	VarArgTable  uint8 = 2 // PF_VATAB — materialized vararg table
)

Vararg model flags (U5b, stock lobject.h PF_*): every vararg function starts hidden-args (VARARGPREP copies func+params above the extras and shifts the frame); a whole-value use / index write / upvalue capture of the named parameter promotes the function to the table model at finish time (needvatab — VARARGPREP then builds {1..n, n=n} into the param slot, GETVARG rewrites to GETTABLE, VARARG gets k=1). NumParameters counts the fixed params only — the vararg parameter itself occupies the register AT NumParameters, reserved by parlist like stock's `adjustlocalvars(ls, 1)` + `luaK_reserveregs`.

View Source
const (
	// BaseLibName is here for consistency; the base functions have no namespace/library.
	BaseLibName = ""
	// LoadLibName is here for consistency; the loading system has no namespace/library.
	LoadLibName = "package"
	// TabLibName is the name of the table Library.
	TabLibName = "table"
	// IoLibName is the name of the io Library.
	IoLibName = "io"
	// OsLibName is the name of the os Library.
	OsLibName = "os"
	// StringLibName is the name of the string Library.
	StringLibName = "string"
	// MathLibName is the name of the math Library.
	MathLibName = "math"
	// DebugLibName is the name of the debug Library.
	DebugLibName = "debug"
	// ChannelLibName is the name of the channel Library.
	ChannelLibName = "channel"
	// CoroutineLibName is the name of the coroutine Library.
	CoroutineLibName = "coroutine"
	// UTF8LibName is the name of the utf8 Library (U3e).
	UTF8LibName = "utf8"
)
View Source
const (
	OP_MOVE       int = iota /*      A B     R[A] := R[B]                                        */
	OP_LOADI                 /*      A sBx   R[A] := sBx                                        */
	OP_LOADF                 /*      A sBx   R[A] := (float)sBx                                 */
	OP_LOADK                 /*      A Bx    R[A] := K[Bx]                                      */
	OP_LOADKX                /*      A       R[A] := K[extra arg]                               */
	OP_LOADFALSE             /*      A       R[A] := false                                      */
	OP_LFALSESKIP            /*      A       R[A] := false; pc++                                */
	OP_LOADTRUE              /*      A       R[A] := true                                       */
	OP_LOADNIL               /*      A B     R[A], R[A+1], ..., R[A+B] := nil                   */
	OP_GETUPVAL              /*      A B     R[A] := UpValue[B]                                 */
	OP_SETUPVAL              /*      A B     UpValue[B] := R[A]                                 */

	OP_GETTABUP /*      A B C   R[A] := UpValue[B][K[C]:string]                       */
	OP_GETTABLE /*      A B C   R[A] := R[B][R[C]]                                     */
	OP_GETI     /*      A B C   R[A] := R[B][C]                                       */
	OP_GETFIELD /*      A B C   R[A] := R[B][K[C]:string]                             */

	OP_SETTABUP /*      A B C   UpValue[A][K[B]:string] := RK(C)                      */
	OP_SETTABLE /*      A B C   R[A][R[B]] := RK(C)                                   */
	OP_SETI     /*      A B C   R[A][B] := RK(C)                                      */
	OP_SETFIELD /*      A B C   R[A][K[B]:string] := RK(C)                            */

	OP_NEWTABLE /*      A vB vC k  R[A] := {}                                          */

	OP_SELF /*      A B C   R[A+1] := R[B]; R[A] := R[B][K[C]:string]                  */

	OP_ADDI /*      A B sC   R[A] := R[B] + sC                                         */

	OP_ADDK  /*      A B C   R[A] := R[B] + K[C]:number                                */
	OP_SUBK  /*      A B C   R[A] := R[B] - K[C]:number                                */
	OP_MULK  /*      A B C   R[A] := R[B] * K[C]:number                                */
	OP_MODK  /*      A B C   R[A] := R[B] % K[C]:number                                */
	OP_POWK  /*      A B C   R[A] := R[B] ^ K[C]:number                                */
	OP_DIVK  /*      A B C   R[A] := R[B] / K[C]:number                                */
	OP_IDIVK /*      A B C   R[A] := R[B] // K[C]:number                               */

	OP_BANDK /*      A B C   R[A] := R[B] & K[C]:integer                               */
	OP_BORK  /*      A B C   R[A] := R[B] | K[C]:integer                               */
	OP_BXORK /*      A B C   R[A] := R[B] ~ K[C]:integer                               */

	OP_SHLI /*      A B sC   R[A] := sC << R[B]                                        */
	OP_SHRI /*      A B sC   R[A] := R[B] >> sC                                        */

	OP_ADD  /*      A B C   R[A] := R[B] + R[C]                                        */
	OP_SUB  /*      A B C   R[A] := R[B] - R[C]                                        */
	OP_MUL  /*      A B C   R[A] := R[B] * R[C]                                        */
	OP_MOD  /*      A B C   R[A] := R[B] % R[C]                                        */
	OP_POW  /*      A B C   R[A] := R[B] ^ R[C]                                        */
	OP_DIV  /*      A B C   R[A] := R[B] / R[C]                                        */
	OP_IDIV /*      A B C   R[A] := R[B] // R[C]                                       */

	OP_BAND /*      A B C   R[A] := R[B] & R[C]                                       */
	OP_BOR  /*      A B C   R[A] := R[B] | R[C]                                       */
	OP_BXOR /*      A B C   R[A] := R[B] ~ R[C]                                       */
	OP_SHL  /*      A B C   R[A] := R[B] << R[C]                                      */
	OP_SHR  /*      A B C   R[A] := R[B] >> R[C]                                      */

	OP_MMBIN  /*      A B C   call C metamethod over R[A] and R[B]                     */
	OP_MMBINI /*      A sB C k  call C metamethod over R[A] and sB                     */
	OP_MMBINK /*      A B C k   call C metamethod over R[A] and K[B]                   */

	OP_UNM  /*      A B     R[A] := -R[B]                                             */
	OP_BNOT /*      A B     R[A] := ~R[B]                                             */
	OP_NOT  /*      A B     R[A] := not R[B]                                          */
	OP_LEN  /*      A B     R[A] := #R[B]                                             */

	OP_CONCAT /*      A B     R[A] := R[A].. ... ..R[A + B - 1]                        */

	OP_CLOSE /*      A       close all upvalues >= R[A]                               */
	OP_TBC   /*      A       mark variable A "to be closed"                           */
	OP_JMP   /*      sJ      pc += sJ                                                */
	OP_EQ    /*      A B k   if ((R[A] == R[B]) ~= k) then pc++                       */
	OP_LT    /*      A B k   if ((R[A] <  R[B]) ~= k) then pc++                       */
	OP_LE    /*      A B k   if ((R[A] <= R[B]) ~= k) then pc++                       */

	OP_EQK /*      A B k   if ((R[A] == K[B]) ~= k) then pc++                          */
	OP_EQI /*      A sB k  if ((R[A] == sB) ~= k) then pc++                            */
	OP_LTI /*      A sB k  if ((R[A] < sB) ~= k) then pc++                             */
	OP_LEI /*      A sB k  if ((R[A] <= sB) ~= k) then pc++                            */
	OP_GTI /*      A sB k  if ((R[A] > sB) ~= k) then pc++                             */
	OP_GEI /*      A sB k  if ((R[A] >= sB) ~= k) then pc++                            */

	OP_TEST    /*      A k     if (not R[A] == k) then pc++                            */
	OP_TESTSET /*      A B k   if (not R[B] == k) then pc++ else R[A] := R[B]          */

	OP_CALL     /*      A B C   R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1])     */
	OP_TAILCALL /*      A B C k  return R[A](R[A+1], ... ,R[A+B-1])                    */

	OP_RETURN  /*      A B C k  return R[A], ... ,R[A+B-2]                             */
	OP_RETURN0 /*              return                                                 */
	OP_RETURN1 /*      A       return R[A]                                            */

	OP_FORLOOP /*      A Bx    update counters; if loop continues then pc-=Bx         */
	OP_FORPREP /*      A Bx    <check values and prepare counters>;
	        if not to run then pc+=Bx+1                                 */

	OP_TFORPREP /*      A Bx    create upvalue for R[A + 3]; pc+=Bx                   */
	OP_TFORCALL /*      A C     R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2])          */
	OP_TFORLOOP /*      A Bx    if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx }       */

	OP_SETLIST /*      A vB vC k  R[A][vC+i] := R[A+i], 1 <= i <= vB                  */

	OP_CLOSURE /*      A Bx    R[A] := closure(KPROTO[Bx])                             */

	OP_VARARG /*      A B C k R[A], ..., R[A+C-2] = varargs                           */

	OP_GETVARG /*      A B C   R[A] := R[B][R[C]], R[B] is vararg parameter           */

	OP_ERRNNIL /*      A Bx    raise error if R[A] ~= nil (K[Bx - 1] is global name)  */

	OP_VARARGPREP /*              (adjust varargs)                                    */

	OP_EXTRAARG /*      Ax      extra (larger) argument for previous opcode           */
)
View Source
const EnvironIndex = -10001
View Source
const FramesPerSegment = 8

FramesPerSegment should be a power of 2 constant for performance reasons. It will allow the go compiler to change the divs and mods into bitshifts. Max is 256 due to current use of uint8 to count how many frames in a segment are used.

View Source
const GlobalsIndex = -10002
View Source
const LNumberBit = 64
View Source
const LNumberScanFormat = "%f"
View Source
const LuaVersion = "Lua 5.5"

U5f: _VERSION re-pinned to the 5.5 core the engine now implements (the 5.2→5.5 program, U1..U5) — a script feature-detecting on it must see the integer/float split, _ENV, and the 5.5 libraries. _GOPHER_LUA_VERSION keeps the implementation identity (the name this fork used before the glasm-era rename; restored at the carve).

View Source
const MultRet = -1
View Source
const PackageAuthors = "Yusuke Inuzuka"
View Source
const PackageName = "GopherLua"
View Source
const RegistryIndex = -10000
View Source
const VarArgAny = VarArgHidden | VarArgTable

VarArgAny is the isvararg(p) test mask (either model marks the proto).

Variables

View Source
var CallStackSize = 256
View Source
var FieldsPerFlush = 50
View Source
var LFalse = LBool(false)
View Source
var LNil = LValue(&LNilType{})
View Source
var LTrue = LBool(true)
View Source
var LuaDirSep string
View Source
var LuaExecDir = "!"
View Source
var LuaIgMark = "-"
View Source
var LuaLDir string
View Source
var LuaOS string
View Source
var LuaPath = "LUA_PATH"
View Source
var LuaPathDefault string
View Source
var LuaPathMark = "?"
View Source
var LuaPathSep = ";"
View Source
var MaxArrayIndex = 67108864
View Source
var MaxTableGetLoop = 100
View Source
var PackageCopyRight = PackageName + " " + PackageVersion + " (Lua 5.5.1 in Go) Copyright (C) 2015 -2017 " + PackageAuthors + " Copyright (C) 2026 Philip Schlump"

PackageCopyRight must stay a var: it is computed from PackageVersion at package init, after the linker has applied any -X override.

View Source
var PackageVersion = "5.5.1-carve.0"

PackageVersion is overridden at link time by the Makefile (make build) via -ldflags "-X github.com/pschlump/gopher-lua.PackageVersion=<version>"; the literal below is the fallback for plain go builds and tests.

View Source
var RegistryGrowStep = 32
View Source
var RegistrySize = 256 * 20

Functions

func FormatVarInfo added in v0.0.7

func FormatVarInfo(kind, name string) string

FormatVarInfo is formatVarInfo exported so the backend renders the identical " (%s '%s')" strings the interp raises.

func FuncNameFromCode added in v0.0.7

func FuncNameFromCode(p *FunctionProto, pc int) (kind, name string)

FuncNameFromCode is funcnamefromcode exported for the backend's call-error hints (the callee name of the instruction at pc).

func GetObjName added in v0.0.7

func GetObjName(p *FunctionProto, lastpc, reg int) (kind, name string)

GetObjName is getobjname exported for the wasm backend's varinfo hints (U5e, row 71): the backend precomputes the interp's debug-name answer at compile time — the raise site's lastpc follows the cf.Pc-1 convention (the instruction that raised; findSetReg itself steps back over an MMBIN* slot at that pc).

func LVAsBool

func LVAsBool(v LValue) bool

LVIsFalse returns false if a given LValue is a nil or false otherwise true.

func LVAsString

func LVAsString(v LValue) string

LVAsString returns string representation of a given LValue if the LValue is a string or number, otherwise an empty string.

func LVCanConvToString

func LVCanConvToString(v LValue) bool

LVCanConvToString returns true if a given LValue is a string or number otherwise false.

func LVIsFalse

func LVIsFalse(v LValue) bool

LVIsFalse returns true if a given LValue is a nil or false otherwise false.

func LocalNameAt added in v0.0.7

func LocalNameAt(p *FunctionProto, localnum, pc int) (string, bool)

LocalNameAt is localNameAt exported for the wasm backend's OP_TBC lowering (U4f): the non-closable raise names the variable, and the backend precomputes the interp's DbgLocals answer at compile time.

func OpCodeMax added in v0.0.7

func OpCodeMax() int

func OpCreateABCk added in v0.0.7

func OpCreateABCk(op, a, b, c, k int) uint32

func OpCreateABx added in v0.0.7

func OpCreateABx(op, a, bx int) uint32

func OpCreateAsBx added in v0.0.7

func OpCreateAsBx(op, a, sbx int) uint32

func OpCreateAx added in v0.0.7

func OpCreateAx(op, ax int) uint32

func OpCreateSJ added in v0.0.7

func OpCreateSJ(op, sj int) uint32

func OpCreatevABCk added in v0.0.7

func OpCreatevABCk(op, a, b, c, k int) uint32

func OpGetArgA added in v0.0.7

func OpGetArgA(inst uint32) int

func OpGetArgAx added in v0.0.7

func OpGetArgAx(inst uint32) int

func OpGetArgB added in v0.0.7

func OpGetArgB(inst uint32) int

func OpGetArgBx added in v0.0.7

func OpGetArgBx(inst uint32) int

func OpGetArgC added in v0.0.7

func OpGetArgC(inst uint32) int

func OpGetArgSbx added in v0.0.7

func OpGetArgSbx(inst uint32) int

func OpGetArgk added in v0.0.7

func OpGetArgk(inst uint32) int

func OpGetArgsJ added in v0.0.7

func OpGetArgsJ(inst uint32) int

func OpGetArgvB added in v0.0.7

func OpGetArgvB(inst uint32) int

func OpGetArgvC added in v0.0.7

func OpGetArgvC(inst uint32) int

func OpGetOpCode added in v0.0.7

func OpGetOpCode(inst uint32) int

func OpenBase

func OpenBase(L *LState) int

func OpenChannel

func OpenChannel(L *LState) int

func OpenCoroutine

func OpenCoroutine(L *LState) int

func OpenDebug

func OpenDebug(L *LState) int

func OpenIo

func OpenIo(L *LState) int

func OpenMath

func OpenMath(L *LState) int

func OpenOs

func OpenOs(L *LState) int

func OpenPackage

func OpenPackage(L *LState) int

func OpenString

func OpenString(L *LState) int

func OpenTable

func OpenTable(L *LState) int

func OpenUTF8 added in v0.0.7

func OpenUTF8(L *LState) int

func UpvalName added in v0.0.7

func UpvalName(p *FunctionProto, uv int) string

UpvalName is upvalName exported for the backend's GETTABUP/SETTABUP object hints (the table operand is an upvalue cell — varinfo's getupvalname arm).

func UpvalueIndex

func UpvalueIndex(i int) int

Types

type ApiError

type ApiError struct {
	Type       ApiErrorType
	Object     LValue
	StackTrace string
	// Underlying error. This attribute is set only if the Type is ApiErrorFile or ApiErrorSyntax
	Cause error
}

func (*ApiError) Error

func (e *ApiError) Error() string

type ApiErrorType

type ApiErrorType int
const (
	ApiErrorSyntax ApiErrorType = iota
	ApiErrorFile
	ApiErrorRun
	ApiErrorError
	ApiErrorPanic
)

type CompileError

type CompileError struct {
	Line    int
	Message string
	// contains filtered or unexported fields
}

func (*CompileError) Error

func (e *CompileError) Error() string

type DbgCall

type DbgCall struct {
	Name string
	Pc   int
	// Method records a colon-syntax call site (`obj:name(...)`): stock's
	// luaL_argerror does not count the implicit self in the argument
	// number and has a dedicated "bad self" error for arg 1 (U3b).
	Method bool
}

type DbgLocalInfo

type DbgLocalInfo struct {
	Name    string
	StartPc int
	EndPc   int
}

type Debug

type Debug struct {
	Name            string
	NameWhat        string
	What            string
	Source          string
	CurrentLine     int
	NUpvalues       int
	LineDefined     int
	LastLineDefined int
	// U5c (stock lua_Debug): 'u' adds nparams/isvararg, 't' adds
	// istailcall/extraargs — extraargs is the __call-chain count (the
	// ci's MAX_CCMT bits, ldebug.c:359), NOT the vararg count: a plain
	// vararg call answers 0, a chain-fed call answers its hop count.
	NParams    int
	IsVarArg   bool
	IsTailCall bool
	ExtraArgs  int
	// 'r' ftransfer/ntransfer (no hooks in the fork: always 0) and 'L'
	// activelines (nil for C functions, collectvalidlines' table).
	FTransfer   int
	NTransfer   int
	ActiveLines *LTable
	// contains filtered or unexported fields
}

type FunctionProto

type FunctionProto struct {
	SourceName string
	// SourceIsNull marks stock's NULL source (U5f): a stripped binary
	// chunk carries no source string, and getinfo/where render "?"
	// shapes for it. A TEXT load with an explicit empty chunkname
	// (load(s, "")) is a REAL empty source — `[string ""]` (db.lua:73)
	// — so "" alone must not take the NULL path. Only the undump sets
	// the flag (dumping an empty TString is size 0, identical to NULL,
	// so every undumped empty source IS stock's NULL).
	SourceIsNull       bool
	LineDefined        int
	LastLineDefined    int
	NumUpvalues        uint8
	NumParameters      uint8
	IsVarArg           uint8
	NumUsedRegisters   uint8
	Code               []uint32
	Constants          []LValue
	FunctionPrototypes []*FunctionProto
	Upvalues           []Upvaldesc

	// EnvUpvalueIndex: the upvalue slot holding _ENV, the table global
	// accesses resolve through (5.2 scheme, upgrade plan D3). -1 when the
	// function references no globals. Used by the loader to seed the
	// main chunk's _ENV cell (GETTABUP/SETTABUP address the cell by the
	// B operand directly).
	EnvUpvalueIndex int8

	DbgSourcePositions []int
	DbgLocals          []*DbgLocalInfo
	DbgCalls           []DbgCall
	DbgUpvalues        []string
	// contains filtered or unexported fields
}

func Compile

func Compile(chunk []ast.Stmt, name string) (proto *FunctionProto, err error)

func (*FunctionProto) String

func (fp *FunctionProto) String() string

func (*FunctionProto) StringConstants added in v0.0.2

func (fp *FunctionProto) StringConstants() []string

StringConstants exposes the string-constants table used by the *KS opcodes and SETGLOBAL (backend access).

type Global

type Global struct {
	MainThread    *LState
	CurrentThread *LState
	Registry      *LTable
	Global        *LTable
	// contains filtered or unexported fields
}

type LBool

type LBool bool

func (LBool) String

func (bl LBool) String() string

func (LBool) Type

func (bl LBool) Type() LValueType

type LChannel

type LChannel chan LValue

func (LChannel) String

func (ch LChannel) String() string

func (LChannel) Type

func (ch LChannel) Type() LValueType

type LFunction

type LFunction struct {
	IsG       bool
	Env       *LTable
	Proto     *FunctionProto
	GFunction LGFunction
	Upvalues  []*Upvalue
}

func (*LFunction) LocalName

func (fn *LFunction) LocalName(regno, pc int) (string, bool)

func (*LFunction) String

func (fn *LFunction) String() string

func (*LFunction) Type

func (fn *LFunction) Type() LValueType

type LGFunction

type LGFunction func(*LState) int

type LInteger added in v0.0.7

type LInteger int64

LInteger is the 64-bit integer subtype of "number" (5.3+, U2/D1). Integer-ness is observable: tostring(2) == "2" while tostring(2.0) == "2.0" (once float printing re-pins at U5), table keys normalize t[2.0] to the integer 2, and //, shifts and integer for-loops require it. Arithmetic on mixed subtypes promotes to float.

func (LInteger) Format added in v0.0.7

func (nm LInteger) Format(f fmt.State, c rune)

fmt.Formatter interface

func (LInteger) String added in v0.0.7

func (nm LInteger) String() string

func (LInteger) Type added in v0.0.7

func (nm LInteger) Type() LValueType

type LNilType

type LNilType struct{}

func (*LNilType) String

func (nl *LNilType) String() string

func (*LNilType) Type

func (nl *LNilType) Type() LValueType

type LNumber

type LNumber float64

func LVAsNumber

func LVAsNumber(v LValue) LNumber

LVAsNumber tries to convert a given LValue to a number (float view). The integer subtype converts to its float value; use parseNumber when the syntactic subtype of a string must be preserved.

func (LNumber) Format

func (nm LNumber) Format(f fmt.State, c rune)

fmt.Formatter interface

func (LNumber) String

func (nm LNumber) String() string

String is the engine-wide float spelling: the stock tostringbuffFloat (%.15g, re-read, %.17g, ".0" on integer-looking results — U3i, the D7 re-pin pulled forward because math.lua's numeral round-trip asserts tostring(1.0) == "1.0"). The old Go dialect printed "1" there.

func (LNumber) Type

func (nm LNumber) Type() LValueType

type LState

type LState struct {
	G       *Global
	Parent  *LState
	Env     *LTable
	Panic   func(*LState)
	Dead    bool
	Options Options
	// contains filtered or unexported fields
}

func NewState

func NewState(opts ...Options) *LState

func (*LState) ArgError

func (ls *LState) ArgError(n int, message string)

func (*LState) Call

func (ls *LState) Call(nargs, nret int)

func (*LState) CallByParam

func (ls *LState) CallByParam(cp P, args ...LValue) error

func (*LState) CallMeta

func (ls *LState) CallMeta(obj LValue, event string) LValue

func (*LState) CheckAny

func (ls *LState) CheckAny(n int) LValue

func (*LState) CheckBool

func (ls *LState) CheckBool(n int) bool

func (*LState) CheckChannel

func (ls *LState) CheckChannel(n int) chan LValue

Checks whether the given index is an LChannel and returns this channel.

func (*LState) CheckFunction

func (ls *LState) CheckFunction(n int) *LFunction

func (*LState) CheckInt

func (ls *LState) CheckInt(n int) int

func (*LState) CheckInt64

func (ls *LState) CheckInt64(n int) int64

func (*LState) CheckNumber

func (ls *LState) CheckNumber(n int) LNumber

func (*LState) CheckOption

func (ls *LState) CheckOption(n int, options []string) int

func (*LState) CheckString

func (ls *LState) CheckString(n int) string

func (*LState) CheckTable

func (ls *LState) CheckTable(n int) *LTable

func (*LState) CheckThread

func (ls *LState) CheckThread(n int) *LState

func (*LState) CheckType

func (ls *LState) CheckType(n int, typ LValueType)

func (*LState) CheckTypes

func (ls *LState) CheckTypes(n int, typs ...LValueType)

func (*LState) CheckUserData

func (ls *LState) CheckUserData(n int) *LUserData

func (*LState) Close

func (ls *LState) Close()

func (*LState) Concat

func (ls *LState) Concat(values ...LValue) string

func (*LState) Context

func (ls *LState) Context() context.Context

Context returns the LState's context. To change the context, use WithContext.

func (*LState) CreateTable

func (ls *LState) CreateTable(acap, hcap int) *LTable

func (*LState) DoFile

func (ls *LState) DoFile(path string) error

func (*LState) DoString

func (ls *LState) DoString(source string) error

func (*LState) Equal

func (ls *LState) Equal(lhs, rhs LValue) bool

func (*LState) Error

func (ls *LState) Error(lv LValue, level int)

This function is equivalent to lua_error( http://www.lua.org/manual/5.1/manual.html#lua_error ).

func (*LState) FindTable

func (ls *LState) FindTable(obj *LTable, n string, size int) LValue

func (*LState) ForEach

func (ls *LState) ForEach(tb *LTable, cb func(LValue, LValue))

func (*LState) GPCall

func (ls *LState) GPCall(fn LGFunction, data LValue) error

func (*LState) Get

func (ls *LState) Get(idx int) LValue

func (*LState) GetFEnv

func (ls *LState) GetFEnv(obj LValue) LValue

func (*LState) GetField

func (ls *LState) GetField(obj LValue, skey string) LValue

func (*LState) GetGlobal

func (ls *LState) GetGlobal(name string) LValue

func (*LState) GetInfo

func (ls *LState) GetInfo(what string, dbg *Debug, fn LValue) (LValue, error)

GetInfo is lua_getinfo over the fork's Debug struct (U5c: stock's option-filtered fields). A leading '>' queries the given function instead of dbg.frame's — stock sets ci=NULL there, so the frame-relative options answer their no-frame defaults ('l' -1, 'n' empty, 't' false/0) while 'S'/'u' still read the function. Returns the 'f' payload (nil without 'f'); the activelines table lands in Debug.ActiveLines ('L'). An option outside "flnSrtuL" returns the invalid-option error the debug library converts to its argerror (stock lua_getinfo answers 0 and db_getinfo raises).

func (*LState) GetLocal

func (ls *LState) GetLocal(dbg *Debug, no int) (string, LValue)

func (*LState) GetMetaField

func (ls *LState) GetMetaField(obj LValue, event string) LValue

func (*LState) GetMetatable

func (ls *LState) GetMetatable(obj LValue) LValue

func (*LState) GetStack

func (ls *LState) GetStack(level int) (*Debug, bool)

GetStack is lua_getstack. Cross-thread queries over a SUSPENDED thread (U5c) run over stock's continuation model: 5.5 keeps the yielded C function's CallInfo alive while the thread is suspended, so level 0 is that C frame and the thread's own frames start at level 1. The fork's Go stack unwinds the yield's G frame instead — the running and normal-at-resume threads keep their boundary frame as a REAL frame, only the suspended ones need the shift. Level 0 hands back a synthetic G frame (C-shaped for 'S'/'u'/'l'/'t'; 'f' on it is not stock's yield function — unpinned corner).

func (*LState) GetTable

func (ls *LState) GetTable(obj LValue, key LValue) LValue

func (*LState) GetTop

func (ls *LState) GetTop() int

func (*LState) GetTypeMetatable

func (ls *LState) GetTypeMetatable(typ string) LValue

func (*LState) GetUpvalue

func (ls *LState) GetUpvalue(fn *LFunction, no int) (string, LValue)

func (*LState) Insert

func (ls *LState) Insert(value LValue, index int)

func (*LState) IsClosed

func (ls *LState) IsClosed() bool

func (*LState) LessThan

func (ls *LState) LessThan(lhs, rhs LValue) bool

func (*LState) Load

func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error)

Load parses, compiles, and wraps a chunk as a callable function. The chunk's _ENV upvalue (D3) is seeded with the thread's environment.

func (*LState) LoadEnv added in v0.0.7

func (ls *LState) LoadEnv(reader io.Reader, name string, env LValue) (*LFunction, error)

LoadEnv is Load with an explicit chunk environment (5.2 load's env argument): the compiled main chunk's _ENV upvalue cell is seeded with env, so the chunk (and every closure it creates) resolves globals there. A nil env falls back to the thread environment; 5.5 accepts ANY value for env (calls.lua:415's `load(..., 123)` — only actual global accesses care that it is a table).

func (*LState) LoadFile

func (ls *LState) LoadFile(path string) (*LFunction, error)

func (*LState) LoadString

func (ls *LState) LoadString(source string) (*LFunction, error)

func (*LState) MemoryUsedBytes added in v0.0.7

func (ls *LState) MemoryUsedBytes() int64

MemoryUsedBytes reports the accounted allocation total (monotone — never refunded; see membudget.go's header).

func (*LState) NewClosure

func (ls *LState) NewClosure(fn LGFunction, upvalues ...LValue) *LFunction

func (*LState) NewFunction

func (ls *LState) NewFunction(fn LGFunction) *LFunction

func (*LState) NewFunctionFromProto

func (ls *LState) NewFunctionFromProto(proto *FunctionProto) *LFunction

func (*LState) NewTable

func (ls *LState) NewTable() *LTable

func (*LState) NewThread

func (ls *LState) NewThread() (*LState, context.CancelFunc)

NewThread returns a new LState that shares with the original state all global objects. If the original state has context.Context, the new state has a new child context of the original state and this function returns its cancel function.

func (*LState) NewTypeMetatable

func (ls *LState) NewTypeMetatable(typ string) *LTable

func (*LState) NewUserData

func (ls *LState) NewUserData() *LUserData

func (*LState) Next

func (ls *LState) Next(tb *LTable, key LValue) (LValue, LValue)

func (*LState) ObjLen

func (ls *LState) ObjLen(v1 LValue) int

func (*LState) OpenLibs

func (ls *LState) OpenLibs()

OpenLibs loads the built-in libraries. It is equivalent to running OpenLoad, then OpenBase, then iterating over the other OpenXXX functions in any order.

func (*LState) OptBool

func (ls *LState) OptBool(n int, d bool) bool

func (*LState) OptChannel

func (ls *LState) OptChannel(n int, ch chan LValue) chan LValue

If the given index is a LChannel, returns this channel. If this argument is absent or is nil, returns ch. Otherwise, raises an error.

func (*LState) OptFunction

func (ls *LState) OptFunction(n int, d *LFunction) *LFunction

func (*LState) OptInt

func (ls *LState) OptInt(n int, d int) int

func (*LState) OptInt64

func (ls *LState) OptInt64(n int, d int64) int64

func (*LState) OptNumber

func (ls *LState) OptNumber(n int, d LNumber) LNumber

func (*LState) OptString

func (ls *LState) OptString(n int, d string) string

func (*LState) OptTable

func (ls *LState) OptTable(n int, d *LTable) *LTable

func (*LState) OptUserData

func (ls *LState) OptUserData(n int, d *LUserData) *LUserData

func (*LState) PCall

func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error)

func (*LState) Pop

func (ls *LState) Pop(n int)

func (*LState) PreloadModule

func (ls *LState) PreloadModule(name string, loader LGFunction)

Set a module loader to the package.preload table.

func (*LState) Push

func (ls *LState) Push(value LValue)

func (*LState) RaiseError

func (ls *LState) RaiseError(format string, args ...interface{})

This function is equivalent to luaL_error( http://www.lua.org/manual/5.1/manual.html#luaL_error ). U2f: the position target is the CURRENT frame when raised from Lua code (luaG_runerror semantics — the VM's own raises) and the caller when raised from a G function (luaL_error semantics) — and a G caller yields NO position (stock's luaL_where over a C frame is empty; the old skipg walk skipped straight past it).

func (*LState) RawEqual

func (ls *LState) RawEqual(lhs, rhs LValue) bool

func (*LState) RawGet

func (ls *LState) RawGet(tb *LTable, key LValue) LValue

func (*LState) RawGetInt

func (ls *LState) RawGetInt(tb *LTable, key int) LValue

func (*LState) RawSet

func (ls *LState) RawSet(tb *LTable, key LValue, value LValue)

func (*LState) RawSetInt

func (ls *LState) RawSetInt(tb *LTable, key int, value LValue)

func (*LState) Register

func (ls *LState) Register(name string, fn LGFunction)

func (*LState) RegisterModule

func (ls *LState) RegisterModule(name string, funcs map[string]LGFunction) LValue

func (*LState) Remove

func (ls *LState) Remove(index int)

func (*LState) RemoveCallerFrame

func (ls *LState) RemoveCallerFrame() *callFrame

RemoveCallerFrame removes the stack frame above the current stack frame. This is useful in tail calls. It returns the new current frame.

func (*LState) RemoveContext

func (ls *LState) RemoveContext() context.Context

RemoveContext removes the context associated with this LState and returns this context.

func (*LState) Replace

func (ls *LState) Replace(idx int, value LValue)

func (*LState) Resume

func (ls *LState) Resume(th *LState, fn *LFunction, args ...LValue) (ResumeState, error, []LValue)

func (*LState) SetContext

func (ls *LState) SetContext(ctx context.Context)

SetContext set a context ctx to this LState. The provided ctx must be non-nil.

func (*LState) SetFEnv

func (ls *LState) SetFEnv(obj LValue, env LValue)

func (*LState) SetField

func (ls *LState) SetField(obj LValue, key string, value LValue)

func (*LState) SetFuncs

func (ls *LState) SetFuncs(tb *LTable, funcs map[string]LGFunction, upvalues ...LValue) *LTable

func (*LState) SetGlobal

func (ls *LState) SetGlobal(name string, value LValue)

func (*LState) SetLocal

func (ls *LState) SetLocal(dbg *Debug, no int, lv LValue) string

func (*LState) SetMemoryBudgetBytes added in v0.0.7

func (ls *LState) SetMemoryBudgetBytes(n int64)

SetMemoryBudgetBytes arms the per-VM accounted memory budget: once set, the allocation seams charge against it and the first write past it raises a pcall-catchable "not enough memory". Zero disables the limit (MemoryUsedBytes keeps reporting the charge total). Must be called before the first run (the limit is not read atomically — see membudget.go's header).

func (*LState) SetMetatable

func (ls *LState) SetMetatable(obj LValue, mt LValue)

func (*LState) SetTable

func (ls *LState) SetTable(obj LValue, key LValue, value LValue)

func (*LState) SetTop

func (ls *LState) SetTop(idx int)

func (*LState) SetUpvalue

func (ls *LState) SetUpvalue(fn *LFunction, no int, lv LValue) string

func (*LState) Status

func (ls *LState) Status(th *LState) string

func (*LState) String

func (ls *LState) String() string

func (*LState) ToBool

func (ls *LState) ToBool(n int) bool

func (*LState) ToChannel

func (ls *LState) ToChannel(n int) chan LValue

Converts the Lua value at the given acceptable index to the chan LValue.

func (*LState) ToFunction

func (ls *LState) ToFunction(n int) *LFunction

func (*LState) ToInt

func (ls *LState) ToInt(n int) int

func (*LState) ToInt64

func (ls *LState) ToInt64(n int) int64

func (*LState) ToNumber

func (ls *LState) ToNumber(n int) LNumber

func (*LState) ToString

func (ls *LState) ToString(n int) string

func (*LState) ToStringMeta

func (ls *LState) ToStringMeta(lv LValue) LValue

ToStringMeta returns string representation of given LValue — the luaL_tolstring port (U3h): __tostring's result must be a string (a number converts, like lua_isstring); the default rendering for tables/functions/etc. is "kind: 0x..." with kind from a string __name metafield when present.

func (*LState) ToTable

func (ls *LState) ToTable(n int) *LTable

func (*LState) ToThread

func (ls *LState) ToThread(n int) *LState

func (*LState) ToUserData

func (ls *LState) ToUserData(n int) *LUserData

func (*LState) Type

func (ls *LState) Type() LValueType

func (*LState) TypeError

func (ls *LState) TypeError(n int, typ LValueType)

TypeError is luaL_typeerror with the fork's type-tag spelling; the actual-operand side honors __name and reports "no value" when the argument is absent.

func (*LState) Where

func (ls *LState) Where(level int) string

func (*LState) XMoveTo

func (ls *LState) XMoveTo(other *LState, n int)

func (*LState) Yield

func (ls *LState) Yield(values ...LValue) int

type LString

type LString string

func (LString) Format

func (st LString) Format(f fmt.State, c rune)

fmt.Formatter interface

func (LString) String

func (st LString) String() string

func (LString) Type

func (st LString) Type() LValueType

type LTable

type LTable struct {
	Metatable LValue
	// contains filtered or unexported fields
}

func (*LTable) Append

func (tb *LTable) Append(value LValue)

Append appends a given LValue to this LTable.

func (*LTable) ForEach

func (tb *LTable) ForEach(cb func(LValue, LValue))

ForEach iterates over this table of elements, yielding each in turn to a given function. Hash entries yield in first-insertion order (deterministic; the old implementation iterated two Go maps in randomized order).

func (*LTable) Insert

func (tb *LTable) Insert(i int, value LValue)

Insert inserts a given LValue at position `i` in this LTable.

func (*LTable) Len

func (tb *LTable) Len() int

Len returns length of this LTable without using __len.

func (*LTable) MaxN

func (tb *LTable) MaxN() int

MaxN returns a maximum number key that nil value does not exist before it.

func (*LTable) Next

func (tb *LTable) Next(key LValue) (LValue, LValue)

This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ). Unlike RawGet/RawSet, the incoming key is NOT normalized: stock 5.5's findindex accepts array entries only for integer keys (keyinarray is ttisinteger-only) and hashes the raw key for the hash part, so the float spelling of an integer slot is an invalid key (baseNext screens it).

func (*LTable) RawGet

func (tb *LTable) RawGet(key LValue) LValue

RawGet returns an LValue associated with a given key without __index metamethod.

func (*LTable) RawGetH

func (tb *LTable) RawGetH(key LValue) LValue

RawGetH returns an LValue associated with a given key without __index metamethod.

func (*LTable) RawGetInt

func (tb *LTable) RawGetInt(key int) LValue

RawGetInt returns an LValue at position `key` without __index metamethod. An out-of-array-range key reads the hash part (stock luaH_getint), so a {[math.mininteger]=10} table answers at negative indices too.

func (*LTable) RawGetString

func (tb *LTable) RawGetString(key string) LValue

RawGetString returns an LValue associated with a given key without __index metamethod. This is the engine's hottest table read (OP_GETFIELD/GETTABUP), so the probe loop lives here: hash, mask, compare — no LValue boxing of the query, no method-call layer.

func (*LTable) RawSet

func (tb *LTable) RawSet(key LValue, value LValue)

RawSet sets a given LValue to a given index without the __newindex metamethod. It is recommended to use `RawSetString` or `RawSetInt` for performance if you already know the given LValue is a string or number.

func (*LTable) RawSetH

func (tb *LTable) RawSetH(key LValue, value LValue)

RawSetH sets a given LValue to a given index without the __newindex metamethod. The one-shot charge mirrors the old dict materialization: it fired on the first non-string hash write, sized by the live string keys at that moment (the old make(map, len(strdict))), whatever the value.

func (*LTable) RawSetInt

func (tb *LTable) RawSetInt(key int, value LValue)

RawSetInt sets a given LValue at a position `key` without the __newindex metamethod.

func (*LTable) RawSetString

func (tb *LTable) RawSetString(key string, value LValue)

RawSetString sets a given LValue to a given string index without the __newindex metamethod. The one-shot charge mirrors the old strdict materialization (it fired on the first string write whatever the value, so it does here).

func (*LTable) Readonly added in v0.0.7

func (tb *LTable) Readonly() bool

Readonly reports the one-way write guard (SetReadonly).

func (*LTable) Remove

func (tb *LTable) Remove(pos int) LValue

Remove removes from this table the element at a given position.

func (*LTable) SetReadonly added in v0.0.7

func (tb *LTable) SetReadonly()

SetReadonly marks this table readonly — the N2 sandbox's one-way guard (the embed's WithGlobalsProtection; the analog of the C engine's interposed luaH_* setters). Lua-visible writes raise "Attempt to modify a readonly table" (the bytecode set paths and everything that funnels into them — LState.SetTable/SetField, rawset, the table library's lua_seti writes; see setField / setFieldString / baseRawSet). Raw Go writes (the LTable methods below, LState.RawSet*) are exempt: that is the host staging seam. One-way by design — there is no unmark.

func (*LTable) String

func (tb *LTable) String() string

func (*LTable) Type

func (tb *LTable) Type() LValueType

type LUserData

type LUserData struct {
	Value     interface{}
	Env       *LTable
	Metatable LValue
	// UserValue is the debug.getuservalue slot (U5c, stock uvalues[0]):
	// any Lua value, nil by default — distinct from Env, the legacy 5.1
	// environment table the fork's GetFEnv reads. UserValues is the slot
	// capacity (stock newuserdatauv's nuvalue): stock's Lua-visible
	// userdata — the io files — are created with ZERO slots, so
	// debug.get/setuservalue answer nil for them (db.lua:434 pins it);
	// NewUserData leaves 0 for the same posture.
	UserValue  LValue
	UserValues int8
	// Light marks a light userdata (U5c, stock LUA_VLIGHTUSERDATA): a
	// bare pointer in Lua's clothing. It still types as "userdata"
	// (5.5's typename table maps both tags to it), but full-userdata
	// argument checks reject it naming "light userdata" (luaL_typeerror
	// special case), base setmetatable refuses it, and its metatable is
	// the per-basic-type table (debug.setmetatable territory), never a
	// per-object one. debug.upvalueid is the only creator.
	Light bool
}

func (*LUserData) String

func (ud *LUserData) String() string

func (*LUserData) Type

func (ud *LUserData) Type() LValueType

type LValue

type LValue interface {
	String() string
	Type() LValueType
}

func IntegerValue added in v0.0.8

func IntegerValue(v LInteger) LValue

IntegerValue boxes an integer as an LValue through the shared small-integer cache ([-1024,1024) boxes are preallocated statics, so count/length-class integers cross allocation-free; outside the window it is a plain one-box conversion). This is the exported seam for embedders — host-package crossings and any API boundary — the interpreter itself uses the allocator method (LInteger2I), which bulk-allocates the out-of-cache range instead.

type LValueType

type LValueType int
const (
	LTNil LValueType = iota
	LTBool
	LTNumber
	LTString
	LTFunction
	LTUserData
	LTThread
	LTTable
	LTChannel
	// LTInteger is the 5.3+ integer subtype (U2/D1). It is a distinct
	// LValueType so type switches can tell int from float, but it names
	// "number": Lua's type() must keep reporting the basic type.
	LTInteger
)

func (LValueType) String

func (vt LValueType) String() string

type Options

type Options struct {
	// Call stack size. This defaults to `lua.CallStackSize`.
	CallStackSize int
	// Data stack size. This defaults to `lua.RegistrySize`.
	RegistrySize int
	// Allow the registry to grow from the registry size specified up to a value of RegistryMaxSize. A value of 0
	// indicates no growth is permitted. The registry will not shrink again after any growth.
	RegistryMaxSize int
	// If growth is enabled, step up by an additional `RegistryGrowStep` each time to avoid having to resize too often.
	// This defaults to `lua.RegistryGrowStep`
	RegistryGrowStep int
	// Controls whether or not libraries are opened by default
	SkipOpenLibs bool
	// Tells whether a Go stacktrace should be included in a Lua stacktrace when panics occur.
	IncludeGoStackTrace bool
	// If `MinimizeStackMemory` is set, the call stack will be automatically grown or shrank up to a limit of
	// `CallStackSize` in order to minimize memory usage. This does incur a slight performance penalty.
	MinimizeStackMemory bool
	// MaxCoroutines caps the number of live coroutines (threads created
	// by coroutine.create/wrap and not yet closed/killed — coroutines
	// are goroutines here, so unbounded creation needs a ceiling).
	// Creating past the cap raises a pcall-catchable "too many
	// coroutines". Zero = unlimited (stock behavior).
	MaxCoroutines int
}

Options is a configuration that is used to create a new LState.

type P

type P struct {
	Fn      LValue
	NRet    int
	Protect bool
	Handler *LFunction
}

type ResumeState

type ResumeState int
const (
	ResumeOK ResumeState = iota
	ResumeYield
	ResumeError
)

type Upvaldesc added in v0.0.7

type Upvaldesc struct {
	InStack bool
	Idx     uint8
	Name    string
}

Upvaldesc is a compiled upvalue-capture descriptor (U2c, stock lobject.h Upvaldesc minus the kind field — <const> attributes arrive with U5): InStack captures the parent's register Idx, otherwise the parent's upvalue Idx. These replace the fork's old pseudo-instructions after OP_CLOSURE.

type Upvalue

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

func (*Upvalue) Close

func (uv *Upvalue) Close()

func (*Upvalue) IsClosed

func (uv *Upvalue) IsClosed() bool

func (*Upvalue) SetValue

func (uv *Upvalue) SetValue(value LValue)

func (*Upvalue) UpvalueID added in v0.0.7

func (uv *Upvalue) UpvalueID() *LUserData

UpvalueID returns the identity object for this upvalue cell — the same cell answers the same light userdata, different cells different ones (stock db_upvalueid's semantics over the Upvalue pointer).

func (*Upvalue) Value

func (uv *Upvalue) Value() LValue

Directories

Path Synopsis
cmd
embedcmp command
Command embedcmp compares native C Lua 5.5.1 embedded via its C API (cmd/embedcmp/c/embedbench55.c → note/native-lua55/embedbench) against this repo's host package, in the shape an application actually uses Lua (Redis EVAL): SHORT scripts compiled once and run over and over with per-run KEYS/ARGV arguments.
Command embedcmp compares native C Lua 5.5.1 embedded via its C API (cmd/embedcmp/c/embedbench55.c → note/native-lua55/embedbench) against this repo's host package, in the shape an application actually uses Lua (Redis EVAL): SHORT scripts compiled once and run over and over with per-run KEYS/ARGV arguments.
glua command
luabench command
Command luabench measures the interpreter and the production EVAL postures for high-frequency scripts — the P0 measurement rig (Option-A plan §5), ported from glasm's cmd/luabench (same scripts, same benchIt cadence, so glasm's recorded numbers are a usable historical reference).
Command luabench measures the interpreter and the production EVAL postures for high-frequency scripts — the P0 measurement rig (Option-A plan §5), ported from glasm's cmd/luabench (same scripts, same benchIt cadence, so glasm's recorded numbers are a usable historical reference).
Package host is the embedder's engine over the pure-Go Lua 5.5.1 interpreter: the Ultima (pure-Go Redis clone) EVAL/SCRIPT posture.
Package host is the embedder's engine over the pure-Go Lua 5.5.1 interpreter: the Ultima (pure-Go Redis clone) EVAL/SCRIPT posture.
Lua pattern match functions for Go
Lua pattern match functions for Go

Jump to

Keyboard shortcuts

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