Skip to content
folbrichtPublic

About

Pure-Go Lua 5.4 for embedding scripts in Go: load once, call from any goroutine. A simple, modern API, no cgo, and built for performance

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

204 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

glua

Go Reference

A pure-Go Lua 5.4 for embedding small scripts in Go programs: rules, policies, handlers and configuration. Scripts are loaded once, frozen, and then called from any goroutine as typed Go functions, under limits on steps, memory and nesting.

Goals

  • Easy to use. A small, modern Go API: generics for typed calls, context.Context for cancellation, plain Go values in and out. Pure Go with no cgo, so it cross-compiles and builds anywhere Go does.
  • Performant. A register VM tuned for the hot paths of embedded scripts, measured against C Lua and other Go implementations (see Performance).

Examples

import lua "github.com/folbricht/glua"

Run Lua from a string

Eval runs a chunk once and converts its result:

n, err := lua.Eval[int64](ctx, `return 6 * 7`) // 42

To call into a script more than once, load it into a Runtime and bind its functions to Go functions:

rt := lua.MustNew()
script, err := rt.Load(ctx, "greet", `
	function greet(name)
		return "hello, " .. name
	end`)
greet, err := script.Func1[string, string]("greet")

s, err := greet(ctx, "world") // "hello, world"

Load a script from a file

Load takes the script's source and a name that error messages use, here the file's:

src, err := os.ReadFile("policy.lua")
rt := lua.MustNew(lua.Limits{Steps: 100_000, AllocBytes: 1 << 20})
script, err := rt.Load(ctx, "policy.lua", string(src))
allow, err := script.Func2[*Request, int64, bool]("allow")

ok, err := allow(ctx, req, 10) // a plain Go function, safe from any goroutine
-- policy.lua
function allow(req, limit)
  return req.method == "GET" and req.cost <= limit
end

Load runs the script's top level once and freezes what it built, so one Script serves every goroutine. The fields of *Request are visible to Lua by their snake_case names.

Call Go from Lua

Go functions are registered as globals when the Runtime is made. Typed adapters such as lua.Fn1 wrap plain Go functions; a lua.GoFunc reads its arguments and sets its results itself:

clamp := lua.GoFunc(func(c *lua.Call) error {
	x, lo, hi := c.Arg[int64](0), c.Arg[int64](1), c.Arg[int64](2)
	return c.Return(min(max(x, lo), hi))
})

rt := lua.MustNew(
	lua.Output(os.Stdout), // where print writes; the default discards
	lua.Global("upper", lua.Fn1(strings.ToUpper)),
	lua.Global("clamp", clamp),
)
script, err := rt.Load(ctx, "main.lua", `
	print(upper("hello"), clamp(15, 0, 10)) -- HELLO   10
	function level(n) return upper("level ") .. clamp(n, 1, 5) end`)

level, err := script.Func1[int64, string]("level") // takes an int64, returns a string
out, err := level(ctx, 7) // "LEVEL 5"

A Go error returned by a host function becomes a Lua error, which the script can catch with pcall, unless the host made it with lua.Terminate to end the call.

Rules over a Go struct

Expressions over a Go struct compile into rules, with misspelt names caught when the rule is made:

rule, err := rt.Expr[Order, bool]("gold", `total > 100 and customer.tier == "gold"`)
ok, err := rule.Eval(ctx, &order)

Requirements: Go 1.27 or later, a 64-bit platform. No cgo.

Status: feature-complete and tested against the Lua 5.4.9 test suite and against C Lua by differential fuzzing. Runtimes run the fast interpreter loop, which trusts bytecode the verifier has checked; Hardened runs the bounds-checked loop instead.

Performance

Time to run a suite of 21 small scripts, relative to C Lua 5.4.9 (geometric mean; lower is faster). Details, per-script numbers and method are in the benchmarks.

Engine Relative to C Lua
C Lua 5.4.9 1.00x
glua 1.23x
wangshu 4.28x
GopherLua 5.21x
golua 5.96x

Documentation

  • Package documentation: the API, with runnable examples.
  • luajson: converting between Lua values and JSON, and a json module for scripts.
  • The language: how glua differs from Lua 5.4, and which libraries it has.
  • Sandbox and limits: what a script can and cannot do, the limits and the fixed caps.
  • GopherLua and glua side by side: how common tasks look in each.
  • Benchmarks: small scripts in C Lua, glua and other Lua implementations for Go, glua's two interpreter loops, and the cost of calls between Go and Lua.

License

BSD 3-Clause, see LICENSE.

About

Pure-Go Lua 5.4 for embedding scripts in Go: load once, call from any goroutine. A simple, modern API, no cgo, and built for performance

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages