Categorygithub.com/Shopify/go-lua
modulepackage
0.0.0-20240527182111-9ab1540f3f5f
Repository: https://github.com/shopify/go-lua.git
Documentation: pkg.go.dev

# README

Build Status GoDoc

A Lua VM in pure Go

go-lua is a port of the Lua 5.2 VM to pure Go. It is compatible with binary files dumped by luac, from the Lua reference implementation.

The motivation is to enable simple scripting of Go applications. For example, it is used to describe flows in Shopify's load generation tool, Genghis.

Usage

go-lua is intended to be used as a Go package. It does not include a command to run the interpreter. To start using the library, run:

go get github.com/Shopify/go-lua

To develop & test go-lua, you'll also need the lua-tests submodule checked out:

git submodule update --init

You can then develop with the usual Go commands, e.g.:

go build
go test -cover

A simple example that loads & runs a Lua script is:

package main

import "github.com/Shopify/go-lua"

func main() {
  l := lua.NewState()
  lua.OpenLibraries(l)
  if err := lua.DoFile(l, "hello.lua"); err != nil {
    panic(err)
  }
}

Status

go-lua has been used in production in Shopify's load generation tool, Genghis, since May 2014, and is also part of Shopify's resiliency tooling.

The core VM and compiler has been ported and tested. The compiler is able to correctly process all Lua source files from the Lua test suite. The VM has been tested to correctly execute over a third of the Lua test cases.

Most core Lua libraries are at least partially implemented. Prominent exceptions are regular expressions, coroutines and string.dump.

Weak reference tables are not and will not be supported. go-lua uses the Go heap for Lua objects, and Go does not support weak references.

Benchmarks

Benchmark results shown here are taken from a Mid 2012 MacBook Pro Retina with a 2.6 GHz Core i7 CPU running OS X 10.10.2, go 1.4.2 and Lua 5.2.2.

The Fibonacci function can be written a few different ways to evaluate different performance characteristics of a language interpreter. The simplest way is as a recursive function:

  function fib(n)
    if n == 0 then
      return 0
    elseif n == 1 then
      return 1
    end
    return fib(n-1) + fib(n-2)
  end

This exercises the call stack implementation. When computing fib(35), go-lua is about 6x slower than the C Lua interpreter. Gopher-lua is about 20% faster than go-lua. Much of the performance difference between go-lua and gopher-lua comes from the inclusion of debug hooks in go-lua. The remainder is due to the call stack implementation - go-lua heap-allocates Lua stack frames with a separately allocated variant struct, as outlined above. Although it caches recently used stack frames, it is outperformed by the simpler statically allocated call stacks in gopher-lua.

  $ time lua fibr.lua
  real  0m2.807s
  user  0m2.795s
  sys   0m0.006s
  
  $ time glua fibr.lua
  real  0m14.528s
  user  0m14.513s
  sys   0m0.031s
  
  $ time go-lua fibr.lua
  real  0m17.411s
  user  0m17.514s
  sys   0m1.287s

The recursive Fibonacci function can be transformed into a tail-recursive variant:

  function fibt(n0, n1, c)
    if c == 0 then
      return n0
    else if c == 1 then
      return n1
    end
    return fibt(n1, n0+n1, c-1)
  end
  
  function fib(n)
    fibt(0, 1, n)
  end

The Lua interpreter detects and optimizes tail calls. This exhibits similar relative performance between the 3 interpreters, though gopher-lua edges ahead a little due to its simpler stack model and reduced bookkeeping.

  $ time lua fibt.lua
  real  0m0.099s
  user  0m0.096s
  sys   0m0.002s

  $ time glua fibt.lua
  real  0m0.489s
  user  0m0.484s
  sys   0m0.005s

  $ time go-lua fibt.lua
  real  0m0.607s
  user  0m0.610s
  sys   0m0.068s

Finally, we can write an explicitly iterative implementation:

  function fib(n)
    if n == 0 then
      return 0
    else if n == 1 then
      return 1
    end
    local n0, n1 = 0, 1
    for i = n, 2, -1 do
      local tmp = n0 + n1
      n0 = n1
      n1 = tmp
    end
    return n1
  end

This exercises more of the bytecode interpreter’s inner loop. Here we see the performance impact of Go’s switch implementation. Both go-lua and gopher-lua are an order of magnitude slower than the C Lua interpreter.

  $ time lua fibi.lua
  real  0m0.023s
  user  0m0.020s
  sys   0m0.003s

  $ time glua fibi.lua
  real  0m0.242s
  user  0m0.235s
  sys   0m0.005s

  $ time go-lua fibi.lua
  real  0m0.242s
  user  0m0.240s
  sys   0m0.028s

License

go-lua is licensed under the MIT license.

# Functions

ArgumentCheck checks whether cond is true.
ArgumentError raises an error with a standard message that includes extraMessage as a comment.
AtPanic sets a new panic function and returns the old one.
BaseOpen opens the basic library.
Bit32Open opens the bit32 library.
CallMeta calls a metamethod.
CheckAny checks whether the function has an argument of any type (including nil) at position index.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
CheckString checks whether the function argument at index is a string and returns this string.
CheckType checks whether the function argument at index has type t.
No description provided by the author
CheckUserData checks whether the function argument at index is a userdata of the type name (see NewMetaTable) and returns the userdata (see ToUserData).
DebugHook returns the current hook function.
DebugHookCount returns the current hook count.
DebugHookMask returns the current hook mask.
DebugOpen opens the debug library.
DoFile loads and runs the given file.
DoString loads and runs the given string.
Errorf raises an error.
FileResult produces the return values for file-related functions in the standard library (io.open, os.rename, file:seek, etc.).
Info gets information about a specific function or function invocation.
IOOpen opens the io library.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
MathOpen opens the math library.
MetaField pushes onto the stack the field event from the metatable of the object at index.
No description provided by the author
No description provided by the author
No description provided by the author
NewMetaTable returns false if the registry already has the key name.
NewState creates a new thread running in a new, independent state.
NewStateEx creates a new Lua state.
OpenLibraries opens all standard libraries.
No description provided by the author
No description provided by the author
OptString returns the string at index if it is a string.
No description provided by the author
OSOpen opens the os library.
PackageOpen opens the package library.
Require calls function f with string name as an argument and sets the call result in package.loaded[name], as if that function had been called through require.
SetDebugHook sets the debugging hook function.
No description provided by the author
No description provided by the author
SetUpValue sets the value of a closure's upvalue.
Stack gets information about the interpreter runtime stack.
StringOpen opens the string library.
No description provided by the author
TableOpen opens the table library.
No description provided by the author
ToStringMeta converts any Lua value at the given index to a Go string in a reasonable format.
Traceback creates and pushes a traceback of the stack l1.
No description provided by the author
UpValue returns the name of the upvalue at index away from function, where index cannot be greater than the number of upvalues.
UpValueId returns a unique identifier for the upvalue numbered n from the closure at index f.
UpValueIndex returns the pseudo-index that represents the i-th upvalue of the running function.
UpValueJoin makes the n1-th upvalue of the Lua closure at index f1 refer to the n2-th upvalue of the Lua closure at index f2.
Version returns the address of the version number stored in the Lua core.
Where pushes onto the stack a string identifying the current position of the control at level in the call stack.

# Constants

Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
Debug.Event and SetDebugHook mask argument values.
MinStack is the minimum Lua stack available to a Go function.
MultipleReturns is the argument for argCount or resultCount in ProtectedCall and Call.
Performs addition (+).
Performs division (/).
Compares for equality (==).
Compares for less or equal (<=).
Compares for less than (<).
Performs modulo (%).
Performs multiplication (*).
Performs exponentiation (^).
Performs subtraction (-).
Performs mathematical negation (unary -).
RegistryIndex is the pseudo-index for the registry table.
RegistryIndexGlobals is the registry index for the global environment.
RegistryIndexMainThread is the registry index for the main thread of the State.
Signature is the mark for precompiled code ('<esc>Lua').
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
Valid Type values.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Variables

Errors introduced by the Lua VM.
Errors introduced by the Lua VM.
Errors introduced by the Lua VM.
Errors introduced by the Lua VM.

# Structs

A Debug carries different pieces of information about a function or an activation record.
A RegistryFunction is used for arrays of functions to be registered by SetFunctions.
A State is an opaque structure representing per thread Lua state.

# Type aliases

A ComparisonOperator is an op argument for Compare.
A Function is a Go function intended to be called from Lua.
A Hook is a callback function that can be registered with SetDebugHook to trace various VM events.
An Operator is an op argument for Arith.
A RuntimeError is an error raised internally by the Lua VM or through Error.
A Type is a symbolic representation of a Lua VM type.