Documentation
¶
Overview ¶
Package luapure is a pure-Go implementation of PUC-Lua 5.4: its instruction set, single-pass compiler, virtual machine, standard libraries, and semantics are ported from the reference Lua sources. It is written from scratch against the PUC 5.4 sources; it is a derivative work of Lua.
Lua is Copyright (C) 1994-2025 Lua.org, PUC-Rio, distributed under the MIT license; that notice is retained in LICENSE-Lua as the license requires. See README.md for the PUC-source-to-file map and the Go-native adaptations.
Package luapure is a ground-up implementation of the Lua 5.4 virtual machine (instruction set, compiler, and interpreter), distinct from the package's historical 5.1-derived engine. This file ports PUC-Lua 5.4.8's lopcodes.h and lopcodes.c verbatim: the 32-bit instruction encoding, the opcode set, and the per-opcode mode table.
Instruction formats (bit 0 is least significant):
iABC C(8) | B(8) | k(1) | A(8) | Op(7) iABx Bx(17) | A(8) | Op(7) iAsBx sBx(17,signed) | A(8) | Op(7) iAx Ax(25) | Op(7) isJ sJ(25,signed) | Op(7)
Example (Register) ¶
Register a Go function that validates its arguments with CheckString, exactly like the standard library functions do.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
L.Register("greet", func(L *luapure.LState) int {
who := L.CheckString(1)
L.Push(luapure.MkString("hello, " + who))
return 1
})
res, _ := L.DoString(`return greet("world")`, "=embed")
fmt.Println(res[0].Str())
}
Output: hello, world
Example (Sandbox) ¶
A sandbox state omits the dangerous libraries and the code-loading globals.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewSandbox()
res, _ := L.DoString(`return load == nil, io == nil, (1 + 2)`, "=embed")
fmt.Println(res[0].AsBool(), res[1].AsBool(), res[2].AsInt())
}
Output: true true 3
Example (Session) ¶
Example_session shows the front-end loop: a breakpoint stops the program, the controller reads a variable, then resumes. Source is fetched by id, never held by the controller.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
db := map[string]string{
"demo": "local x = 21\nlocal y = x * 2\nreturn y\n",
}
L := luapure.NewState()
L.OpenLibs()
sess := luapure.NewSession(L, func(id string) (string, bool) { s, ok := db[id]; return s, ok })
sess.SetBreakpoints("demo", []int{2}) // stop at `local y = x * 2`
done := sess.Start(db["demo"], "=demo")
for {
select {
case st := <-sess.Stops():
x, _ := sess.Eval(0, "x")
fmt.Printf("stopped at %s:%d, x=%s\n", st.Source, st.Line, x)
sess.Continue()
case r := <-done:
if r.Err != nil {
fmt.Println("error:", r.Err)
return
}
fmt.Printf("result=%d\n", r.Values[0].AsInt())
return
}
}
}
Output: stopped at demo:2, x=21 result=42
Example (TableExchange) ¶
Build a table in Go, hand it to a script as a global, and read the results back — the core embedding round-trip.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
cfg := luapure.NewTable()
cfg.SetStr("name", luapure.MkString("luapure"))
cfg.SetStr("level", luapure.Int(5))
L.SetGlobal("config", cfg.Value())
res, err := L.DoString(`return config.name, config.level * 2`, "=embed")
if err != nil {
panic(err)
}
fmt.Println(res[0].Str(), res[1].AsInt())
}
Output: luapure 10
Example (Userdata) ¶
Bind a Go type into Lua as userdata: register a named metatable once, give it a method table, then hand scripts a userdata value they call methods on. The method recovers the Go value type-checked with CheckUserData.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
type counter struct{ n int64 }
L := luapure.NewState()
L.OpenLibs()
mt, _ := L.NewMetatable("Counter")
methods := luapure.NewTable()
methods.SetStr("inc", luapure.NewGoFunc("inc", func(L *luapure.LState) int {
c := L.CheckUserData(1, "Counter").(*counter)
c.n += L.OptInt(2, 1)
L.Push(luapure.Int(c.n))
return 1
}))
mt.SetStr("__index", methods.Value())
L.SetGlobal("c", L.NewUserData(&counter{}, mt))
res, err := L.DoString(`c:inc(); c:inc(10); return c:inc()`, "=embed")
if err != nil {
panic(err)
}
fmt.Println(res[0].AsInt())
}
Output: 12
Example (Uservalue) ¶
A uservalue keeps a Lua value associated with (and reachable from) a userdatum. Here a script stashes a label on the object and reads it back; the host sees the same slot through UserValue.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
mt, _ := L.NewMetatable("Box")
ud := L.NewUserDataUV(struct{}{}, 1, mt)
L.SetGlobal("box", ud)
if _, err := L.DoString(`debug.setuservalue(box, "hello", 1)`, "=embed"); err != nil {
panic(err)
}
v, _ := ud.UserValue(1)
fmt.Println(v.Str())
}
Output: hello
Index ¶
- Constants
- Variables
- func Disasm(p *Proto) string
- func DisasmInst(i Instruction) string
- func FromValue(v Value) any
- func GetArgA(i Instruction) int
- func GetArgAx(i Instruction) int
- func GetArgB(i Instruction) int
- func GetArgBx(i Instruction) int
- func GetArgC(i Instruction) int
- func GetArgk(i Instruction) int
- func GetArgsB(i Instruction) int
- func GetArgsBx(i Instruction) int
- func GetArgsC(i Instruction) int
- func GetArgsJ(i Instruction) int
- func Int2sC(i int) int
- func LuacInst(i Instruction) string
- func LuacListing(p *Proto) []string
- func SC2int(i int) int
- func SetArgA(i *Instruction, v int)
- func SetArgAx(i *Instruction, v int)
- func SetArgB(i *Instruction, v int)
- func SetArgBx(i *Instruction, v int)
- func SetArgC(i *Instruction, v int)
- func SetArgk(i *Instruction, v int)
- func SetArgsBx(i *Instruction, v int)
- func SetArgsJ(i *Instruction, v int)
- func SetOpCode(i *Instruction, o OpCode)
- func Testk(i Instruction) bool
- func VersionString() string
- type BinOpr
- type Closure
- type CompileError
- type Debugger
- func (d *Debugger) AddBreakpoint(source string, line int)
- func (d *Debugger) ClearBreakpoints()
- func (d *Debugger) Continue()
- func (d *Debugger) Detach()
- func (d *Debugger) Frames() []Frame
- func (d *Debugger) OnStop(fn func(d *Debugger, reason StopReason))
- func (d *Debugger) Pause()
- func (d *Debugger) RemoveBreakpoint(source string, line int)
- func (d *Debugger) SetBreakpoints(source string, lines []int)
- func (d *Debugger) StepInto()
- func (d *Debugger) StepOut()
- func (d *Debugger) StepOver()
- type Frame
- func (f Frame) CurrentLine() int
- func (f Frame) Eval(expr string) ([]Value, error)
- func (f Frame) Func() Value
- func (f Frame) FuncName() (name, what string)
- func (f Frame) IsLua() bool
- func (f Frame) IsTailCall() bool
- func (f Frame) IsVararg() bool
- func (f Frame) Level() int
- func (f Frame) Local(n int) (name string, v Value, ok bool)
- func (f Frame) Locals() []Local
- func (f Frame) NumParams() int
- func (f Frame) NumUpvalues() int
- func (f Frame) SetLocal(n int, v Value) bool
- func (f Frame) SetUpvalue(n int, v Value) bool
- func (f Frame) ShortSource() string
- func (f Frame) Source() string
- func (f Frame) Upvalue(n int) (name string, v Value, ok bool)
- func (f Frame) Vararg(n int) (v Value, ok bool)
- type FrameInfo
- type FuncState
- type GoFunc
- type GoHook
- type GoPanicError
- type HookEvent
- type HookMask
- type Instruction
- type LState
- func (L *LState) Arg(n int) Value
- func (L *LState) ArgError(n int, msg string)
- func (L *LState) Call(fn Value, args []Value, nresults int) ([]Value, error)
- func (L *LState) CallProto(p *Proto, nresults int) ([]Value, error)
- func (L *LState) CallProtoEnv(p *Proto, env *Table, nresults int) ([]Value, error)
- func (L *LState) CallValue(fn Value, args []Value, nresults int) []Value
- func (L *LState) CheckBool(n int) bool
- func (L *LState) CheckInt(n int) int64
- func (L *LState) CheckNumber(n int) float64
- func (L *LState) CheckString(n int) string
- func (L *LState) CheckTable(n int) *Table
- func (L *LState) CheckUserData(n int, name string) any
- func (L *LState) ClearGoHook()
- func (L *LState) ClearInstructionLimit()
- func (L *LState) Close()
- func (L *LState) Context() context.Context
- func (L *LState) DoFile(path string) ([]Value, error)
- func (L *LState) DoString(src, name string) ([]Value, error)
- func (L *LState) Frame(level int) (f Frame, ok bool)
- func (L *LState) GetGlobal(name string) Value
- func (L *LState) GetMetatable(name string) *Table
- func (L *LState) Globals() *Table
- func (L *LState) Index(t, key Value) Value
- func (L *LState) InstructionCount() uint64
- func (L *LState) LoadFile(path string) (*Proto, error)
- func (L *LState) NArgs() int
- func (L *LState) NewMetatable(name string) (mt *Table, created bool)
- func (L *LState) NewUserData(data any, meta *Table) Value
- func (L *LState) NewUserDataUV(data any, nuv int, meta *Table) Value
- func (L *LState) OpenBase()
- func (L *LState) OpenCoroutine()
- func (L *LState) OpenDebug()
- func (L *LState) OpenIO()
- func (L *LState) OpenLibs()
- func (L *LState) OpenMath()
- func (L *LState) OpenOS()
- func (L *LState) OpenString()
- func (L *LState) OpenTable()
- func (L *LState) OpenUTF8()
- func (L *LState) OptInt(n int, def int64) int64
- func (L *LState) OptString(n int, def string) string
- func (L *LState) Preload(name string, open func(*LState) *Table)
- func (L *LState) Push(v Value)
- func (L *LState) RaiseError(format string, args ...any) int
- func (L *LState) RaiseValue(v Value) int
- func (L *LState) Register(name string, fn GoFunc)
- func (L *LState) Requiref(name string, open func(*LState) *Table, glb bool) *Table
- func (L *LState) RunWith(env *Table, src, name string) ([]Value, error)
- func (L *LState) SetContext(ctx context.Context)
- func (L *LState) SetGlobal(name string, v Value)
- func (L *LState) SetGoHook(h GoHook, mask HookMask, count int)
- func (L *LState) SetIndex(t, key, val Value)
- func (L *LState) SetInstructionLimit(n uint64)
- func (L *LState) SetRecoverGoPanics(on bool)
- func (L *LState) SetUserMetatable(v Value, meta *Table)
- func (L *LState) StackDepth() int
- func (L *LState) TestUserData(n int, name string) any
- func (L *LState) ToValue(x any) Value
- func (L *LState) TypeError(n int, want string)
- type LocVar
- type Local
- type LuaError
- type OpCode
- type OpMode
- type Option
- type Proto
- type RunResult
- type Session
- func (s *Session) Continue()
- func (s *Session) Debugger() *Debugger
- func (s *Session) Eval(level int, expr string) (string, error)
- func (s *Session) Pause()
- func (s *Session) SetBreakpoints(source string, lines []int)
- func (s *Session) Snippet(id string, line, ctx int) (string, bool)
- func (s *Session) Source(id string) (text string, ok bool)
- func (s *Session) Stack() []FrameInfo
- func (s *Session) Start(src, chunkName string) <-chan RunResult
- func (s *Session) StepInto()
- func (s *Session) StepOut()
- func (s *Session) StepOver()
- func (s *Session) Stops() <-chan StopState
- func (s *Session) Variables(level int) []Var
- type SourceResolver
- type StopReason
- type StopState
- type TMS
- type Table
- func (t *Table) Get(key Value) Value
- func (t *Table) GetInt(i int64) Value
- func (t *Table) GetStr(name string) Value
- func (t *Table) Len() int64
- func (t *Table) Next(key Value) (k, v Value, ok bool)
- func (t *Table) Set(key, val Value)
- func (t *Table) SetInt(i int64, val Value)
- func (t *Table) SetStr(name string, val Value)
- func (t *Table) Value() Value
- type UnOpr
- type UpvalDesc
- type Upvalue
- type Value
- func (v Value) AsBool() bool
- func (v Value) AsFloat() float64
- func (v Value) AsInt() int64
- func (v Value) AsTable() *Table
- func (v Value) AsUserData() any
- func (v Value) IsBool() bool
- func (v Value) IsFalsy() bool
- func (v Value) IsFloat() bool
- func (v Value) IsFunction() bool
- func (v Value) IsInt() bool
- func (v Value) IsNil() bool
- func (v Value) IsNumber() bool
- func (v Value) IsString() bool
- func (v Value) IsTable() bool
- func (v Value) IsThread() bool
- func (v Value) IsUserData() bool
- func (v Value) NumUserValues() int
- func (a Value) RawEqual(b Value) bool
- func (v Value) SetUserValue(n int, val Value) bool
- func (v Value) Str() string
- func (v Value) Type() int
- func (v Value) UserMetatable() *Table
- func (v Value) UserValue(n int) (val Value, ok bool)
- type Var
- type ZIO
Examples ¶
Constants ¶
const ( TM_INDEX = iota TM_NEWINDEX TM_GC TM_MODE TM_LEN TM_EQ TM_ADD TM_SUB TM_MUL TM_MOD TM_POW TM_DIV TM_IDIV TM_BAND TM_BOR TM_BXOR TM_SHL TM_SHR TM_UNM TM_BNOT TM_LT TM_LE TM_CONCAT TM_CALL TM_CLOSE TM_N )
--- tag-method codes (ltm.h TMS, ORDER TM) ---
Only the arithmetic/bitwise events are used by the compiler (stamped into the MMBIN family so the VM can find the right metamethod); the full enum is defined here for the VM to reuse.
const ( VVOID expkind = iota // empty expression (no value) VNIL // constant nil VTRUE // constant true VFALSE // constant false VK // constant in 'k'; info = index VKFLT // float constant; nval VKINT // integer constant; ival VKSTR // string constant; strval VNONRELOC // value in a fixed register; info = register VLOCAL // local variable; vr.ridx register, vr.vidx actvar slot VUPVAL // upvalue; info = upvalue index VCONST // compile-time <const>; info = actvar slot VINDEXED // t[k]: ind.t table register, ind.idx key register VINDEXUP // upvalue[k]: ind.t table upvalue, ind.idx key K index VINDEXI // t[i]: ind.t table register, ind.idx integer key VINDEXSTR // t.field: ind.t table register, ind.idx key K index VJMP // test/comparison; info = pc of the jump VRELOC // result can go in any register; info = instruction pc VCALL // function call; info = instruction pc VVARARG // vararg; info = instruction pc )
const ( TypeNil = 0 TypeBoolean = 1 TypeLightUserData = 2 TypeNumber = 3 TypeString = 4 TypeTable = 5 TypeFunction = 6 TypeUserData = 7 TypeThread = 8 )
Basic Lua types (lua.h LUA_T*), as reported by Value.Type() and `type()`.
const ( VarKindReg uint8 = iota // regular variable VarKindConst // <const> VarKindToClose // <close> (to-be-closed) VarKindCTConst // compile-time constant )
Upvalue kinds (PUC lparser.h VDKREG / RDKCONST / RDKTOCLOSE / RDKCTC).
const ( SizeC = 8 SizeB = 8 SizeBx = SizeC + SizeB + 1 // 17 SizeA = 8 SizeAx = SizeBx + SizeA // 25 SizeJ = SizeBx + SizeA // 25 (sJ) SizeOp = 7 )
Field sizes (bits) — lopcodes.h SIZE_*.
const ( PosOp = 0 PosA = PosOp + SizeOp // 7 PosK = PosA + SizeA // 15 PosB = PosK + 1 // 16 PosC = PosB + SizeB // 24 PosBx = PosK // 15 PosAx = PosA // 7 PosJ = PosA // 7 (sJ) )
Field positions (bit offsets) — lopcodes.h POS_*.
const ( MaxArgBx = (1 << SizeBx) - 1 // 131071 OffsetsBx = MaxArgBx >> 1 // 65535 MaxArgAx = (1 << SizeAx) - 1 // 33554431 MaxArgJ = (1 << SizeJ) - 1 // 33554431 OffsetsJ = MaxArgJ >> 1 // 16777215 MaxArgA = (1 << SizeA) - 1 // 255 MaxArgB = (1 << SizeB) - 1 // 255 MaxArgC = (1 << SizeC) - 1 // 255 OffsetsC = MaxArgC >> 1 // 127 )
Argument limits — lopcodes.h MAXARG_* / OFFSET_*.
const ( LuaVersion = "Lua 5.4" // == _VERSION; PUC LUA_VERSION (DO NOT CHANGE) LuaRelease = "5.4.8" // PUC patch level this engine tracks Version = "0.1.2" // luapure implementation version )
Version identifiers. LuaVersion is the language version exposed to Lua as the global _VERSION; it mirrors PUC's LUA_VERSION exactly ("Lua 5.4") and must never carry an implementation suffix, since scripts and libraries compare it for exact equality (the PUC test suite's all.lua bails out on _VERSION ~= "Lua 5.4"). The luapure implementation version is exposed separately, both here and to Lua via the _LUAPURE_VERSION global.
const ( OpAdd = iota // + OpSub // - OpMul // * OpMod // % OpPow // ^ OpDiv // / OpIDiv // // OpBAnd // & OpBOr // | OpBXor // ~ (binary) OpShl // << OpShr // >> OpUnm // unary - OpBNot // ~ (unary) )
Arithmetic operation codes, matching lua.h LUA_OP* (ORDER OP). The values are significant: BinOpr/UnOpr map onto this range by a fixed offset (see lcode.go binopr2op / constfolding).
const LFieldsPerFlush = 50
LFieldsPerFlush is the number of list items accumulated before a SETLIST.
const NoReg = MaxArgA
NoReg is an invalid register that still fits in 8 bits (lopcodes.h NO_REG).
const NumOpCodes = int(OP_EXTRAARG) + 1
NumOpCodes is the count of opcodes (lopcodes.h NUM_OPCODES).
Variables ¶
var ( True = Value{/* contains filtered or unexported fields */} False = Value{/* contains filtered or unexported fields */} )
True and False are the boolean values.
var ( // MaxStack bounds the value stack (PUC LUAI_MAXSTACK), so unbounded // recursion raises "stack overflow" instead of exhausting memory. MaxStack = 1000000 // ErrorStackReserve is slack above MaxStack (PUC ERRORSTACKSIZE) kept free // so that, after a "stack overflow" is raised, the message handler and any // to-be-closed variables still have room to run. ErrorStackReserve = 200 // MaxCCalls bounds nested Go-level calls (metamethods, pcall, hooks, native // callbacks), matching PUC's LUAI_MAXCCALLS. Exceeding it raises a catchable // error rather than letting Go recursion overflow the real stack and abort. MaxCCalls = 200 // LoadBufferSize is the disk read-block size for streaming file loads (PUC // getF reads BUFSIZ at a time); a file is fed to the compiler one block at a // time so a large source is lexed incrementally and never held whole. LoadBufferSize = 8192 // IDSize is the length to which chunk source names are truncated in // messages (PUC LUA_IDSIZE), via luaO_chunkid / shortSrc. IDSize = 60 // MaxTagLoop limits __index/__newindex metamethod chains (lvm.c MAXTAGLOOP) // before raising "'__index' chain too long; possible loop". MaxTagLoop = 2000 // MaxTableArraySize, when > 0, caps how far a table's array part may grow // before extending it raises a catchable "not enough memory" error. Go // cannot turn a real allocation failure into a recoverable error (OOM is a // fatal runtime throw), so a program that fills a table without bound (Lua's // heavy.lua toomanyidx: `for i=1,math.huge do a[i]=i end`) would crash the // host process instead of erroring. This ceiling stands in for PUC's // malloc-failure path. 0 (the default) preserves unlimited growth. MaxTableArraySize int // MaxLexElement, when > 0, caps the length of a single lexical element (an // identifier, string, or number token). PUC's save raises "lexical element // too long" when the token buffer would grow past MAX_SIZE; on a 64-bit // build that is effectively unreachable, so an unbounded token (heavy.lua's // hugeid, a reader feeding one endless identifier) OOMs the host instead. // This ceiling raises the catchable error first. 0 (the default) keeps the // unbounded behavior. MaxLexElement int )
var Nil = Value{/* contains filtered or unexported fields */}
Nil is the nil value.
Functions ¶
func Disasm ¶
Disasm renders a Proto's bytecode as a numbered listing, one instruction per line: "<pc> [<line>] NAME operands".
func DisasmInst ¶
func DisasmInst(i Instruction) string
DisasmInst renders a single instruction as "NAME operands", decoding operands according to the opcode's format (lopcodes.h getOpMode). The k flag, when set, is appended as "k". This is a compact, deterministic form for tests and debugging — not the exact `luac -l` listing.
func FromValue ¶
FromValue converts a Lua Value into a Go value: nil, bool, int64, float64, string, or — for a table — a map[any]any whose keys and values are themselves converted (shared/cyclic tables map to the same Go map). Functions, threads, and userdata are returned as their Value unchanged.
Example ¶
FromValue converts a Lua table to a map[any]any.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
v := L.ToValue(map[string]any{"x": 42})
m := luapure.FromValue(v).(map[any]any)
fmt.Println(m["x"])
}
Output: 42
func GetArgA ¶
func GetArgA(i Instruction) int
func GetArgAx ¶
func GetArgAx(i Instruction) int
func GetArgB ¶
func GetArgB(i Instruction) int
func GetArgBx ¶
func GetArgBx(i Instruction) int
func GetArgC ¶
func GetArgC(i Instruction) int
func GetArgk ¶
func GetArgk(i Instruction) int
func GetArgsB ¶
func GetArgsB(i Instruction) int
func GetArgsBx ¶
func GetArgsBx(i Instruction) int
func GetArgsC ¶
func GetArgsC(i Instruction) int
func GetArgsJ ¶
func GetArgsJ(i Instruction) int
func LuacInst ¶
func LuacInst(i Instruction) string
LuacInst renders one instruction with exactly the operand columns `luac -l` prints (lopcodes/luac.c PrintCode), minus the trailing "; ..." comment. This lets the codegen tests diff our output against the reference compiler. The k flag is shown as luac does: a "k" suffix on the last operand for the store/self/return family, or a separate 0/1 column for the test family.
func LuacListing ¶
LuacListing renders a prototype tree as a flat list of luac-style instruction strings in luac's preorder (each function followed by its nested functions), suitable for diffing against parsed `luac -l` output.
func SetArgA ¶
func SetArgA(i *Instruction, v int)
func SetArgAx ¶
func SetArgAx(i *Instruction, v int)
func SetArgB ¶
func SetArgB(i *Instruction, v int)
func SetArgBx ¶
func SetArgBx(i *Instruction, v int)
func SetArgC ¶
func SetArgC(i *Instruction, v int)
func SetArgk ¶
func SetArgk(i *Instruction, v int)
func SetArgsBx ¶
func SetArgsBx(i *Instruction, v int)
func SetArgsJ ¶
func SetArgsJ(i *Instruction, v int)
func SetOpCode ¶
func SetOpCode(i *Instruction, o OpCode)
func Testk ¶
func Testk(i Instruction) bool
func VersionString ¶
func VersionString() string
VersionString returns the full implementation banner, e.g. "luapure 0.1.2 (Lua 5.4.8)". It is the value exposed to Lua as the global _LUAPURE_VERSION.
Types ¶
type BinOpr ¶
type BinOpr int
BinOpr enumerates binary operators in the order lcode.c expects (ORDER OPR); the layout lets binopr2op/binopr2TM convert to opcodes/tag methods by offset.
type Closure ¶
type Closure struct {
// contains filtered or unexported fields
}
Closure is a callable value (Value.tag == tagFunction).
type CompileError ¶
CompileError is raised when a program exceeds a hard compiler limit (too many registers, control structure too long, …). The top-level compile recovers it.
func (*CompileError) Error ¶
func (e *CompileError) Error() string
type Debugger ¶
type Debugger struct {
L *LState
// contains filtered or unexported fields
}
Debugger is a breakpoint/step controller built on the Go debug hook (SetGoHook). It is the substrate a front end — a DAP server, a CLI console — drives: set breakpoints, run, and on each stop inspect frames and choose how to resume.
Control flow and goroutines. The hook fires on the goroutine running the VM. When a stop condition is met (a breakpoint, a completed step, or an async Pause) the Debugger invokes the OnStop handler *synchronously on that goroutine* and blocks the VM there until the handler returns. Inside the handler the program is frozen, so frames (Frames / the LState's Frame) can be read safely; the handler chooses the next action with Continue / StepInto / StepOver / StepOut and returns to resume. A front end running on another goroutine (reading a socket) coordinates by having the handler block on a channel it feeds — the action is still issued on the VM goroutine, which keeps frame access race-free.
Methods that configure the session (SetBreakpoints, AddBreakpoint, Pause) are safe to call from another goroutine while the VM runs. The resume actions and frame inspection are meant to be used from within the OnStop handler.
func NewDebugger ¶
NewDebugger attaches a debugger to L and installs its line hook. The session starts in run mode (stops only at breakpoints or Pause). Call Detach to remove the hook.
func (*Debugger) AddBreakpoint ¶
AddBreakpoint adds a single source:line breakpoint.
func (*Debugger) ClearBreakpoints ¶
func (d *Debugger) ClearBreakpoints()
ClearBreakpoints removes every breakpoint.
func (*Debugger) Continue ¶
func (d *Debugger) Continue()
Continue resumes free execution until the next breakpoint or Pause.
func (*Debugger) Detach ¶
func (d *Debugger) Detach()
Detach removes the debug hook; the program then runs unobserved.
func (*Debugger) Frames ¶
Frames returns the live call frames, innermost first (level 0 .. depth-1). The returned frames are valid only until the handler resumes.
func (*Debugger) OnStop ¶
func (d *Debugger) OnStop(fn func(d *Debugger, reason StopReason))
OnStop sets the handler invoked (on the VM goroutine, blocking) each time the debugger stops. The handler inspects state and calls one resume action.
func (*Debugger) Pause ¶
func (d *Debugger) Pause()
Pause requests that the debugger stop at the next line it executes. It is asynchronous: call it from another goroutine while the VM runs.
func (*Debugger) RemoveBreakpoint ¶
RemoveBreakpoint removes a single source:line breakpoint.
func (*Debugger) SetBreakpoints ¶
SetBreakpoints replaces the breakpoint set for source with lines. A breakpoint matches a frame whose raw chunk name or short name equals source, so "@f.lua", "f.lua" and a "=name" chunk are all addressable.
func (*Debugger) StepInto ¶
func (d *Debugger) StepInto()
StepInto resumes until the next source line in any frame (descending into calls).
type Frame ¶
type Frame struct {
L *LState
// contains filtered or unexported fields
}
Frame is a handle to a live call frame. It is valid only transiently — while the VM is paused inside a hook the frame belongs to — because the underlying call-info and stack slots are reused once execution resumes. Do not retain a Frame past the hook that produced it.
func (Frame) CurrentLine ¶
CurrentLine returns the source line the frame is stopped on, or -1 for a native frame or one with stripped line info.
func (Frame) Eval ¶
Eval compiles and runs a Lua expression in the scope of the frame, the way a debugger console's "print" or a watch expression does: a bare name resolves to the frame's locals first, then its upvalues, then globals, and an assignment writes back to whichever it found (or to a new global). The expression's results are returned; a statement (e.g. "x = 1") is accepted too.
Eval is for use while the program is paused at the frame (from a Debugger OnStop handler). The debug hook is suppressed for the duration, so evaluating does not itself trip breakpoints or recurse into the handler.
func (Frame) FuncName ¶
FuncName returns the frame function's name and namewhat as debug.getinfo's "n" option computes them from the call site (e.g. "f", "method"); both are empty when no name can be inferred.
func (Frame) IsTailCall ¶
IsTailCall reports whether the frame was entered by a tail call (so its caller frame was replaced).
func (Frame) Local ¶
Local returns the frame's n-th local variable (1-based), the lua_getlocal form: its source name (or "(temporary)"/"(vararg)") and current value. ok is false when n is out of range.
func (Frame) Locals ¶
Locals enumerates the frame's active local variables in declaration order (the indices Local/SetLocal accept). Unnamed in-range slots appear as "(temporary)"; callers wanting only source-named locals can skip names that begin with "(".
func (Frame) NumParams ¶
NumParams returns the number of fixed parameters of a Lua frame's function (0 for a native frame).
func (Frame) NumUpvalues ¶
NumUpvalues returns the count of upvalues of the frame's function.
func (Frame) SetLocal ¶
SetLocal assigns the frame's n-th local variable (1-based), the lua_setlocal form. It reports false when n is out of range.
func (Frame) SetUpvalue ¶
SetUpvalue assigns the frame function's n-th upvalue (1-based). It reports false when n is out of range.
func (Frame) ShortSource ¶
ShortSource returns the chunk name formatted for display (shortSrc), matching the short_src field of debug.getinfo.
func (Frame) Source ¶
Source returns the frame function's chunk name in PUC's raw form ("@file", "=name", or the source string); "=[C]" for a native frame.
type FrameInfo ¶
type FrameInfo struct {
Level int
Source string // chunk id (short name)
Line int
Func string
What string // namewhat: "local", "global", "method", "" ...
}
FrameInfo is a snapshot of one call frame (Session.Stack).
type FuncState ¶
type FuncState struct {
// contains filtered or unexported fields
}
FuncState holds the state needed to generate code for one function. The fields here are those the lcode machinery touches; codegen.go adds the parser-side bookkeeping (blocks, active variables, upvalue resolution) it needs.
type GoFunc ¶
GoFunc is a native (C-style) function: it reads its arguments from and pushes its results onto L's stack, returning the number of results, exactly like a lua_CFunction.
type GoHook ¶
GoHook is a debug callback. It runs inline on the goroutine executing the VM, with no extra Lua frame pushed, so it can inspect the current frame (via L.Frame) directly. For a line event, line is the source line; otherwise it is -1. The hook may call back into the VM (e.g. L.Call to evaluate an expression) — nested events do not re-enter the hook while it runs.
type GoPanicError ¶
type GoPanicError struct{ Value any }
GoPanicError wraps a non-LuaError Go panic that the protected call recovered in protected mode (SetRecoverGoPanics / WithRecoverGoPanics). Value is the recovered panic value. It surfaces from Call/DoString via errors.As; inside Lua the same error appears as its Error() string.
func (*GoPanicError) Error ¶
func (e *GoPanicError) Error() string
type HookEvent ¶
type HookEvent uint8
HookEvent identifies why a GoHook fired, mirroring PUC's LUA_HOOK* events.
type HookMask ¶
type HookMask int
HookMask selects which events fire a GoHook; OR the constants together. It mirrors PUC's LUA_MASK* bits.
type Instruction ¶
type Instruction uint32
Instruction is a single 32-bit Lua 5.4 VM instruction.
func CreateABCk ¶
func CreateABCk(o OpCode, a, b, c, k int) Instruction
func CreateABx ¶
func CreateABx(o OpCode, a, bx int) Instruction
func CreateAx ¶
func CreateAx(o OpCode, a int) Instruction
func CreatesJ ¶
func CreatesJ(o OpCode, j, k int) Instruction
type LState ¶
type LState struct {
// contains filtered or unexported fields
}
LState is a Lua 5.4 execution state (one thread). It owns the value stack, the chain of CallInfo frames, the open-upvalue and to-be-closed lists, and a reference to the shared globals. Coroutines (multiple threads sharing a global_State) are out of scope for this first VM; LState fuses the per-thread and global state into one object.
func NewSandbox ¶
func NewSandbox() *LState
NewSandbox returns a state with only the safe standard libraries open: base, string, table, math, utf8, and coroutine. The io, os, debug, and package libraries are not opened, and the base globals that load or run arbitrary code or files (load, loadfile, dofile) are removed. collectgarbage is kept. For per-call confinement of a specific chunk, also use RunWith with a custom _ENV; for a time limit, SetContext.
NewSandbox() is shorthand for NewState(WithSandbox()); use the option form to combine it with other options, e.g. NewState(WithSandbox(), WithContext(ctx)).
func NewState ¶
NewState builds a fresh state with an empty globals table and registry.
With no options it opens no libraries and takes its limits from the package globals (luaconf.go) — identical to earlier releases; call OpenLibs yourself. Options layer on top: WithOpenLibs/WithSandbox open libraries, WithContext sets a cancellation context, and WithMaxStack/WithMaxCCalls/ WithMaxTableArraySize override those limits for this State only.
Example (Options) ¶
NewState options fold setup into one call: open the standard libraries and cap a limit for this state only. WithMaxStack/WithMaxCCalls/ WithMaxTableArraySize override the package globals for this state, leaving other states on the process-wide defaults.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState(luapure.WithOpenLibs(), luapure.WithMaxStack(50000))
res, err := L.DoString(`return math.max(2, 7)`, "=opts")
if err != nil {
panic(err)
}
fmt.Println(res[0].AsInt())
}
Output: 7
func (*LState) ArgError ¶
ArgError raises a "bad argument #n to '<func>' (<msg>)" error for the current native call (luaL_argerror).
func (*LState) Call ¶
Call invokes fn with the given arguments under a protected call (pcall semantics), returning up to nresults results (multRet for all). A Lua error is returned as a *LuaError instead of propagating.
Example ¶
A Lua error from a protected Call comes back as a *LuaError carrying the raised value.
package main
import (
"errors"
"fmt"
"strings"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
res, _ := L.DoString(`return function() error("boom") end`, "=embed")
fn := res[0]
_, err := L.Call(fn, nil, 0)
var le *luapure.LuaError
fmt.Println(errors.As(err, &le), strings.Contains(le.Error(), "boom"))
}
Output: true true
func (*LState) CallProto ¶
CallProto runs a compiled main-chunk prototype under a protected call, returning up to nresults results (multRet for all).
func (*LState) CallProtoEnv ¶
CallProtoEnv runs a compiled chunk with a custom _ENV table, returning up to nresults results (multRet for all). It is RunWith for an already-compiled Proto: the chunk's global accesses resolve through env instead of the state's own globals. A pool can compile once (cache the *Proto) and pass a fresh env per call to confine that call's global writes (the Lua 5.4 _ENV sandbox), without recompiling. Build env as setmetatable({}, {__index = baseline}) to fall back to shared library/globals while keeping per-call writes isolated.
func (*LState) CallValue ¶
CallValue invokes fn with the given arguments, returning up to nresults results. Intended for use from native functions (e.g. pcall, pairs).
CallValue is unprotected: a Lua error raised by fn propagates as a panic (caught by an enclosing protected call). Embedders calling into Lua from Go should use Call, which runs fn protected and returns the error.
func (*LState) CheckInt ¶
CheckInt returns argument n as an integer, else raises an error. A number with no integer representation is rejected (luaL_checkinteger).
func (*LState) CheckNumber ¶
CheckNumber returns argument n as a float, else raises an error (luaL_checknumber).
func (*LState) CheckString ¶
CheckString returns argument n as a string, coercing a number, else raises a "bad argument" error (luaL_checkstring).
func (*LState) CheckTable ¶
CheckTable returns argument n as a *Table, else raises an error (luaL_checktype with LUA_TTABLE).
func (*LState) CheckUserData ¶
CheckUserData returns argument n's Go payload after verifying its metatable is the registry metatable named name; otherwise it raises a "bad argument" type error reporting name as the expected type (luaL_checkudata). Use it to safely unwrap the Go value a method received as self.
func (*LState) ClearInstructionLimit ¶
func (L *LState) ClearInstructionLimit()
ClearInstructionLimit removes any instruction cap (and its counter).
func (*LState) Close ¶
func (L *LState) Close()
Close releases OS resources the state holds. It closes any non-standard file the state opened as its default I/O (e.g. a file passed to io.output), then forces a GC cycle and drains pending __gc finalizers so handles no longer referenced by the script are closed too. Because lua-pure delegates collection to the Go runtime, handles still reachable from the state are closed when the state itself becomes unreachable. Close is idempotent; do not run the state afterwards.
Example ¶
Close releases the state's resources and is idempotent.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
L.Close()
L.Close()
fmt.Println("ok")
}
Output: ok
func (*LState) Context ¶
Context returns the cancellation context set by SetContext (nil if none). A Go callback uses it to make its own blocking work cancellable — e.g. select on Context().Done(), or pass it to a context-aware API — so a blocking call obeys the same deadline as the VM. Without this, a blocking callback ignores SetContext/ExecTimeout (the VM only checks the context between instructions). Returns the running coroutine's inherited context when called from one.
func (*LState) DoFile ¶
DoFile compiles and runs the file at path as a main chunk under a protected call, returning its results (the dofile convenience).
Example ¶
DoFile loads and runs a script from disk.
package main
import (
"fmt"
"os"
"path/filepath"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
dir, _ := os.MkdirTemp("", "luapure")
defer os.RemoveAll(dir)
path := filepath.Join(dir, "s.lua")
if err := os.WriteFile(path, []byte(`return 1 + 2 + 3`), 0o644); err != nil {
panic(err)
}
res, err := L.DoFile(path)
if err != nil {
panic(err)
}
fmt.Println(res[0].AsInt())
}
Output: 6
func (*LState) Frame ¶
Frame returns the call frame at the given level: 0 is the currently executing frame, 1 its caller, and so on. ok is false when level is out of range.
func (*LState) GetMetatable ¶
GetMetatable returns the registry metatable registered under name, or nil when NewMetatable has not created it yet (luaL_getmetatable).
func (*LState) Index ¶
Index returns t[key] honouring the __index metamethod chain (the indexing a Lua program sees). t may be any value; a non-indexable value raises an error.
func (*LState) InstructionCount ¶
InstructionCount returns the approximate number of instructions executed under the current limit (finGCPoll granularity), or 0 if no limit is set.
func (*LState) LoadFile ¶
LoadFile compiles the file at path into a prototype without running it, streaming it from disk (text or a precompiled binary chunk, like loadfile). A compile or read error is returned; a Lua syntax error comes back as a *LuaError.
func (*LState) NewMetatable ¶
NewMetatable returns the metatable registered under name in the registry, creating an empty one — with its __name set to name — on the first call. created reports whether this call made it, so an embedder installs the methods exactly once (luaL_newmetatable). The name doubles as the type label in "bad argument" errors raised by CheckUserData.
func (*LState) NewUserData ¶
NewUserData wraps an arbitrary Go value as full userdata carrying meta as its metatable (nil for none). The payload is opaque to Lua scripts, which reach it only through the metatable (e.g. __index methods, __gc). If meta carries a __gc field a finalizer is registered the same way lua_setmetatable does, so the embedder's __gc runs when the object becomes unreachable.
func (*LState) NewUserDataUV ¶
NewUserDataUV is NewUserData with nuv associated Lua-value slots (uservalues), the lua_newuserdatauv form. The slots start nil and are read and written with UserValue / SetUserValue (and the script-facing debug.getuservalue/ setuservalue). Unlike the Go payload, a uservalue is a Lua value the GC tracks — use it to keep a Lua object (a callback, a config table) alive alongside the userdatum and reachable from it without a separate registry entry.
func (*LState) OpenBase ¶
func (L *LState) OpenBase()
OpenBase installs the base library into the globals table.
func (*LState) OpenCoroutine ¶
func (L *LState) OpenCoroutine()
func (*LState) OpenLibs ¶
func (L *LState) OpenLibs()
OpenLibs installs the standard libraries available by default (linit.c). openPackage runs early so the libraries opened after it register themselves in package.loaded.
func (*LState) OpenString ¶
func (L *LState) OpenString()
OpenString installs the string library and the string metatable.
func (*LState) OptInt ¶
OptInt returns argument n as an integer, or def when it is absent or nil (luaL_optinteger).
func (*LState) OptString ¶
OptString returns argument n as a string, or def when it is absent or nil (luaL_optstring).
func (*LState) Preload ¶
Preload registers open as a lazy loader for module name: open is not called until the first require(name), which then caches the module in package.loaded like any other require. This is the embedding-side analog of setting package.preload[name] to a luaopen_* function in PUC. The package library must be open (OpenLibs); otherwise this is a no-op.
func (*LState) RaiseError ¶
RaiseError raises a Lua error from a Go callback with a formatted message, prefixed with the caller's position ("source:line: "), like PUC luaL_error. It does not return (the error unwinds via panic/recover); the int result lets a callback write `return L.RaiseError("bad value %v", v)`, mirroring PUC's `return luaL_error(...)`.
func (*LState) RaiseValue ¶
RaiseValue raises v as the error object, like PUC lua_error — use it to raise a non-string error such as a structured error table. As with RaiseError it does not return; the int result is for `return L.RaiseValue(t.Value())`.
func (*LState) Requiref ¶
Requiref opens a host-provided module the way the standard libraries install themselves (the embedding-side analog of PUC's luaL_requiref). It calls open once to build the module table, caches it in package.loaded[name] so a later require(name) returns the same table, and — when glb is true — also binds it to the global name. It is idempotent: an already-loaded module is returned without calling open again. The package library must be open (OpenLibs) for the require/loaded integration; the global binding works regardless. Returns the module table.
L.Requiref("mylib", func(L *luapure.LState) *luapure.Table {
t := luapure.NewTable()
t.SetStr("greet", luapure.NewGoFunc("greet", greet))
return t
}, true) // installs global `mylib`, and require("mylib") returns it
func (*LState) RunWith ¶
RunWith compiles and runs src as a main chunk whose _ENV is env, under a protected call, returning all results. Expose only the globals you trust in env (build it with NewTable) to confine untrusted code; pair with NewSandbox and SetContext for library and time limits.
Example ¶
RunWith confines a chunk to a custom _ENV: it sees only the globals you put in env.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
env := luapure.NewTable()
env.SetStr("x", luapure.Int(21))
res, err := L.RunWith(env, `return x * 2`, "=embed")
if err != nil {
panic(err)
}
fmt.Println(res[0].AsInt())
}
Output: 42
func (*LState) SetContext ¶
SetContext installs ctx as the cancellation context for this state and the coroutines it spawns. While set, the VM checks ctx.Err() every finGCPoll instructions and raises a catchable error once the context is done, so even a tight infinite loop (`while true do end`) can be interrupted. Pass nil to disable. Cancellation is observed only between VM instructions, not inside a blocking Go call (e.g. a long io read).
Example ¶
A cancelled context interrupts even a tight infinite loop.
package main
import (
"context"
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
ctx, cancel := context.WithCancel(context.Background())
cancel() // already cancelled before we run
L.SetContext(ctx)
_, err := L.DoString(`while true do end`, "=embed")
fmt.Println(err != nil)
}
Output: true
func (*LState) SetGoHook ¶
SetGoHook installs h as the Go debug hook, firing on the events named by mask; when mask includes MaskCount, count is the instruction quantum between count events. A nil h (or zero mask) removes the hook. It is independent of any Lua debug.sethook hook, which keeps firing alongside it.
func (*LState) SetInstructionLimit ¶
SetInstructionLimit caps how many bytecode instructions this state (and the coroutines it spawns, which share the budget) may execute before the VM raises a catchable "instruction limit exceeded" error. It is a host-only guard against runaway pure-Lua CPU, orthogonal to SetContext's wall-clock cancellation: set ExecTimeout-style deadlines with SetContext and a CPU cap here. n == 0 disables it. Setting a limit resets the counter, so call it once per run (like SetContext). The cap is enforced at the finalizer-poll gate, so its granularity is finGCPoll instructions, not exact.
It is deliberately Go-level only — not exposed to Lua via debug.sethook — because a script could otherwise remove its own count hook and defeat the cap.
func (*LState) SetRecoverGoPanics ¶
SetRecoverGoPanics toggles protected mode: when on, a protected call (Call, DoString, the Lua-level pcall, metamethods) that hits a non-LuaError Go panic — typically a registered Go callback that panicked — recovers it into a catchable *GoPanicError and unwinds the VM to its pre-call state, so the panic does not escape to the host and the State remains reusable. When off (the default) such a panic is re-raised unchanged, which is PUC-faithful (PUC does not catch a C-side abort) and surfaces genuine host bugs. A pool that runs less-trusted callbacks typically turns this on. Inherited by coroutines.
func (*LState) SetUserMetatable ¶
SetUserMetatable replaces a full userdata's metatable (nil to clear it), registering a __gc finalizer if the new metatable carries one — the lua_setmetatable path. It is a no-op for non-userdata values.
func (*LState) StackDepth ¶
StackDepth reports how many active call frames there are (the current frame plus its callers). Frame levels run 0 .. StackDepth()-1.
func (*LState) TestUserData ¶
TestUserData is the non-raising CheckUserData (luaL_testudata): it returns the payload when argument n is full userdata whose metatable is the registry metatable named name, else nil. A nil result is also returned for a userdata whose payload is genuinely nil; when that distinction matters, compare the argument's UserMetatable against GetMetatable directly.
func (*LState) ToValue ¶
ToValue converts a Go value into a Lua Value. Supported: nil, bool, the integer and float kinds, string, []any (a 1-based array table), and map[string]any / map[any]any (a keyed table); nested slices/maps recurse. A Value passes through unchanged. Cyclic Go structures are not supported.
Example ¶
ToValue converts Go data into Lua tables; AsTable reads them back.
package main
import (
"fmt"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
v := L.ToValue([]any{10, 20, 30})
t := v.AsTable()
fmt.Println(t.Len(), t.GetInt(2).AsInt())
}
Output: 3 20
type LocVar ¶
type LocVar struct {
Name string
StartPc int // first pc where the variable is active
EndPc int // first pc where it is dead
}
LocVar is a local-variable debug record (PUC lobject.h LocVar): the register holding it is implied by declaration order; StartPc..EndPc is its live range.
type Local ¶
type Local struct {
Name string // a source name, or "(temporary)" / "(vararg)" for unnamed slots
Index int // 1-based index for Frame.Local / Frame.SetLocal
Value Value
}
Local pairs a frame-local variable's name with its current value (Frame.Locals).
type LuaError ¶
type LuaError struct {
// contains filtered or unexported fields
}
LuaError carries a Lua error value through Go's panic/recover (the longjmp replacement noted in the migration plan). It is the error type returned by the protected entry points (DoString, CallProto, Call), so embedders can type-assert to *LuaError to recover the raised Lua value and traceback.
func (*LuaError) Traceback ¶
Traceback returns the captured stack traceback, or "" if none was attached.
type OpCode ¶
type OpCode int
OpCode identifies a Lua 5.4 virtual-machine instruction.
const ( OP_MOVE OpCode = iota OP_LOADI OP_LOADF OP_LOADK OP_LOADKX OP_LOADFALSE OP_LFALSESKIP OP_LOADTRUE OP_LOADNIL OP_GETUPVAL OP_SETUPVAL OP_GETTABUP OP_GETTABLE OP_GETI OP_GETFIELD OP_SETTABUP OP_SETTABLE OP_SETI OP_SETFIELD OP_NEWTABLE OP_SELF OP_ADDI OP_ADDK OP_SUBK OP_MULK OP_MODK OP_POWK OP_DIVK OP_IDIVK OP_BANDK OP_BORK OP_BXORK OP_SHRI OP_SHLI OP_ADD OP_SUB OP_MUL OP_MOD OP_POW OP_DIV OP_IDIV OP_BAND OP_BOR OP_BXOR OP_SHL OP_SHR OP_MMBIN OP_MMBINI OP_MMBINK OP_UNM OP_BNOT OP_NOT OP_LEN OP_CONCAT OP_CLOSE OP_TBC OP_JMP OP_EQ OP_LT OP_LE OP_EQK OP_EQI OP_LTI OP_LEI OP_GTI OP_GEI OP_TEST OP_TESTSET OP_CALL OP_TAILCALL OP_RETURN OP_RETURN0 OP_RETURN1 OP_FORLOOP OP_FORPREP OP_TFORPREP OP_TFORCALL OP_TFORLOOP OP_SETLIST OP_CLOSURE OP_VARARG OP_VARARGPREP OP_EXTRAARG )
The opcode set, in PUC order (lopcodes.h). "Grep ORDER OP" if changed.
func GetOpCode ¶
func GetOpCode(i Instruction) OpCode
func (OpCode) SetsTopOut ¶
SetsTopOut reports whether the opcode sets L->top for the next instruction (relevant when C == 0).
type Option ¶
type Option func(*buildOpts)
Option configures a State created by NewState.
func WithContext ¶
WithContext installs ctx as the State's cancellation context (equivalent to SetContext); the coroutines it spawns inherit it.
func WithMaxCCalls ¶
WithMaxCCalls overrides MaxCCalls for this State only: the nested Go-call depth (metamethods, pcall, hooks, resume) before "C stack overflow".
func WithMaxStack ¶
WithMaxStack overrides MaxStack for this State only: the value-stack ceiling that turns unbounded recursion into a catchable "stack overflow".
func WithMaxTableArraySize ¶
WithMaxTableArraySize overrides MaxTableArraySize for this State only: the array-part growth ceiling that stands in for PUC's malloc-failure path. 0 means unlimited.
func WithOpenLibs ¶
func WithOpenLibs() Option
WithOpenLibs opens the full standard library (equivalent to calling OpenLibs on the new State).
func WithRecoverGoPanics ¶
func WithRecoverGoPanics() Option
WithRecoverGoPanics turns on protected mode (equivalent to SetRecoverGoPanics): a non-LuaError Go panic from a registered callback is recovered into a catchable *GoPanicError and the VM is unwound cleanly, instead of escaping to the host. Off by default (PUC-faithful re-panic); useful for pools that must survive a panicking callback.
func WithSandbox ¶
func WithSandbox() Option
WithSandbox opens only the safe standard libraries, exactly as NewSandbox does: base, string, table, math, utf8, and coroutine, with load/loadfile/ dofile removed. Pair with WithContext and RunWith to confine untrusted code.
type Proto ¶
type Proto struct {
NumParams uint8 // number of fixed (named) parameters
IsVararg bool
MaxStackSize uint8 // registers needed by this function
Code []Instruction // opcodes
Constants []Value // constant table (k)
Protos []*Proto // functions defined inside this one (p)
Upvalues []UpvalDesc // upvalue descriptors
// Debug information.
Source string // chunk name
LineDefined int // 0 for the main chunk
LastLineDef int // last line of the definition
LineInfo []int32 // source line per instruction (1:1 with Code)
LocVars []LocVar // local-variable debug records
}
Proto is a compiled Lua 5.4 function prototype (PUC lobject.h Proto): the bytecode plus the constants, nested prototypes, upvalue descriptors and debug information a closure is instantiated from.
func CompileReader ¶
CompileReader compiles Lua source streamed from r into a prototype, the io.Reader analogue of CompileString. It accepts either text or a precompiled binary chunk and consumes r incrementally rather than buffering it whole.
Example ¶
CompileReader compiles source streamed from any io.Reader.
package main
import (
"fmt"
"strings"
luapure "github.com/htcom-code/lua-pure/lua"
)
func main() {
L := luapure.NewState()
L.OpenLibs()
p, err := luapure.CompileReader(strings.NewReader(`return 6 * 7`), "=embed")
if err != nil {
panic(err)
}
res, _ := L.CallProto(p, 1)
fmt.Println(res[0].AsInt())
}
Output: 42
func CompileString ¶
CompileString parses and compiles Lua source into a prototype. The chunk name is used for error positions and debug info (PUC's "@file" / "=name" forms).
func (*Proto) AddConstant ¶
AddConstant appends v to the constant table and returns its index. (Dedup is the compiler's job; this is the raw append.)
type Session ¶
type Session struct {
L *LState
// contains filtered or unexported fields
}
Session is a synchronous façade over a Debugger for building a debug front end — an MCP server, a DAP adapter, a CLI console. The Debugger's stop handler runs on the VM goroutine and blocks it; Session lets a controller on another goroutine drive that paused VM with ordinary request/response calls, marshalling each inspection onto the VM goroutine so frame access stays safe.
Source on the server only. A Session never needs the script text to debug: breakpoints and stop locations are keyed by a chunk's *name* (an opaque id such as "script/42") and line, and line numbers come from the compiled prototype. The source text is needed only to *display* where execution stopped, and that is served on demand from wherever the host keeps it (a database, an object store) through the SourceResolver — so a remote client that holds no source can still show context by asking the Session. This mirrors DAP's sourceReference mechanism.
Protocol. Launch with Start and select over Stops() and the returned result channel. On each StopState, inspect with Stack / Variables / Eval, then issue exactly one resume (Continue / StepInto / StepOver / StepOut). Inspection and resume calls are valid only while paused at a stop — between receiving a StopState and resuming. SetBreakpoints and Pause are safe to call anytime.
func NewSession ¶
func NewSession(L *LState, resolve SourceResolver) *Session
NewSession attaches a debug session to L. resolve may be nil (no source serving). It installs the debugger's hook; the program runs only once Start (or the host's own call into the VM) executes a chunk.
func (*Session) Continue ¶
func (s *Session) Continue()
Continue resumes until the next breakpoint or Pause.
func (*Session) Debugger ¶
Debugger exposes the underlying controller (e.g. for ClearBreakpoints/Detach).
func (*Session) Eval ¶
Eval evaluates a Lua expression (or statement) in the scope of the frame at level and returns its results rendered for display (tab-separated).
func (*Session) SetBreakpoints ¶
SetBreakpoints replaces the breakpoints for source (a chunk's short id).
func (*Session) Snippet ¶
Snippet renders the lines of chunk id around line (± ctx lines), each prefixed with its number and a marker ("->") on the current line — enough for a front end to show the stop location without ever holding the source itself.
func (*Session) Source ¶
Source returns the text of chunk id via the SourceResolver, or ok=false when there is no resolver or the id is unknown. A remote client that holds no source calls this to display where it stopped.
func (*Session) Start ¶
Start runs src as a chunk named chunkName on a new goroutine and returns a channel that receives the single result when it finishes. chunkName is the debug identity of the script: prefix it with "=" so its short name (which breakpoints and StopState use) is the bare id, e.g. "=script/42".
func (*Session) StepInto ¶
func (s *Session) StepInto()
StepInto resumes to the next line, descending into calls.
func (*Session) StepOut ¶
func (s *Session) StepOut()
StepOut resumes until execution returns to a shallower frame.
func (*Session) StepOver ¶
func (s *Session) StepOver()
StepOver resumes to the next line at this depth or shallower.
type SourceResolver ¶
SourceResolver returns the text of the chunk named id (its short name, e.g. "script/42"), or ok=false when unknown. A host backs it with a DB lookup.
type StopReason ¶
type StopReason uint8
StopReason explains why the debugger stopped.
const ( StopBreakpoint StopReason = iota // a set breakpoint was hit StopStep // a step (into/over/out) completed StopPause // an async Pause request landed )
func (StopReason) String ¶
func (r StopReason) String() string
type StopState ¶
type StopState struct {
Reason StopReason
Source string // innermost frame's chunk id (short name); "" for a native frame
Line int // innermost frame's current line (-1 if unavailable)
Func string // innermost frame's function name, if known
Depth int // number of active frames
}
StopReason values are reused from the Debugger. StopState describes one stop.
type Table ¶
type Table struct {
// contains filtered or unexported fields
}
Table is the Lua 5.4 table object. PUC (ltable.c) uses a hybrid of a packed array part plus an open-addressed node array; this port keeps the same observable semantics with a Go-native split — a 1-based array part for dense integer keys and a map for everything else — which is simpler and equivalent for the VM and the test suite (Lua does not specify hash iteration order).
Keys are normalised exactly as PUC: a float key with an integral value is stored under the equivalent integer key (t[2.0] and t[2] are the same slot), and NaN / nil keys are rejected on assignment.
func (*Table) Next ¶
Next iterates raw key/value pairs the way Lua's next does: pass Nil to start, then pass back the previous key. ok is false once iteration is exhausted. Like next, the traversal order is unspecified and the table must not have keys inserted during iteration (assigning nil to the current key is allowed).
func (*Table) Set ¶
Set assigns t[key] = val without invoking metamethods (raw access). Setting a nil value removes the key.
type UpvalDesc ¶
type UpvalDesc struct {
Name string // upvalue name (debug)
InStack bool // captured from the enclosing function's registers (vs its upvalues)
Index uint8 // register index (InStack) or outer upvalue index
Kind uint8 // variable kind: regular / <const> / <close> / compile-time const
}
UpvalDesc describes one upvalue of a Proto (PUC lobject.h Upvaldesc).
type Upvalue ¶
type Upvalue struct {
// contains filtered or unexported fields
}
Upvalue is a shared variable reference (PUC UpVal). While "open" it aliases a live stack slot (L.stack[idx]); once "closed" it owns the value in `v`. Because the stack is addressed by index rather than by pointer, stack growth never invalidates an open upvalue — no pointer fix-up is needed.
type Value ¶
type Value struct {
// contains filtered or unexported fields
}
Value is a Lua 5.4 runtime value. It uses a tagged-struct representation: scalars are stored inline so integer/float arithmetic does not box, and GC objects are held through a normal pointer (GC-safe, unlike true NaN-boxing which Go's precise GC cannot follow).
Layout (24 bytes on 64-bit): tag(1) + pad + scalar(8) + gc(8).
func (Value) AsTable ¶
AsTable returns the underlying *Table when v is a table, else nil. Pair it with IsTable to distinguish a non-table from an empty table.
func (Value) AsUserData ¶
AsUserData returns the Go payload of a full userdata Value, or nil when v is not full userdata (lua_touserdata). Light userdata yields nil — it carries no Go payload. Pair it with IsUserData to tell a nil payload from a non-userdata.
func (Value) IsFunction ¶
func (Value) IsUserData ¶
func (Value) NumUserValues ¶
NumUserValues reports how many uservalue slots v was created with, or 0 when v is not full userdata.
func (Value) RawEqual ¶
RawEqual reports primitive (no-metamethod) equality, matching PUC luaV_equalobj for the cases representable so far. Integer and float compare by mathematical value (1 == 1.0); strings compare by contents.
func (Value) SetUserValue ¶
SetUserValue assigns the n-th uservalue slot (1-based) of a full userdata, the lua_setiuservalue form. It reports false (assigning nothing) when v is not userdata or n is out of range.
func (Value) UserMetatable ¶
UserMetatable returns the metatable attached to a full userdata Value, or nil (when v is not userdata or has none).
type Var ¶
type Var struct {
Name string
Value string // display rendering of the value
Kind string // "local" | "upvalue" | "vararg"
Raw Value
}
Var is a rendered variable binding (Session.Variables). Raw carries the underlying value so a front end can render it differently or expand a table into its fields (e.g. a DAP variablesReference tree); it is valid only while the program is paused at the stop the Var was read from.
type ZIO ¶
type ZIO struct {
// contains filtered or unexported fields
}
ZIO is a byte stream backed by an optional refill reader (PUC struct Zio). reader returns the next piece and ok=false at end of input; a nil/empty piece also ends the stream, matching PUC's treatment of a NULL/zero-size reader result as EOZ.
Source Files
¶
- convert.go
- debug_session.go
- debug_session_driver.go
- disasm.go
- doc.go
- lapi.go
- lapi_check.go
- lapi_debug.go
- lapi_load.go
- lapi_options.go
- lapi_sandbox.go
- lapi_table.go
- lapi_userdata.go
- lauxlib.go
- lbaselib.go
- lcode.go
- lcorolib.go
- ldblib.go
- ldebug.go
- ldo.go
- ldo_hook.go
- ldump.go
- lfunc.go
- lgc.go
- lgc_weak.go
- lgc_weakkey.go
- linit.go
- liolib.go
- llex.go
- lmathlib.go
- loadlib.go
- lobject.go
- lobject_gc.go
- lobject_num.go
- lobject_proto.go
- lopcodes.go
- loslib.go
- lparser.go
- lparser_codegen.go
- lstate.go
- lstrlib.go
- lstrlib_pack.go
- lstrlib_pattern.go
- ltable.go
- ltablib.go
- ltm.go
- luaconf.go
- lutf8lib.go
- lvm.go
- lvm_arith.go
- lvm_execute.go
- lzio.go