Skip to content

wippyai/go-lua

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-lua

Heavily modified Fork of gopher-lua. Lua 5.1 VM with integers/bitwise from 5.3, plus a custom flow-sensitive type checker. Optimized for actor runtimes with shared immutable stdlib and low per-state overhead.

go get github.com/wippyai/go-lua

Type Annotations

local x: number = 42
local name: string = "alice"
local items: {number} = {1, 2, 3}
local lookup: {[string]: number} = {a = 1, b = 2}

type Point = {x: number, y: number}
local p: Point = {x = 10, y = 20}

local fn: (number, number) -> number = function(a, b) return a + b end

Generics

local function first<T>(arr: {T}): T?
    return arr[1]
end

local n = first({1, 2, 3})      -- integer?
local s = first({"a", "b"})     -- string?

type Box<T> = {value: T}
local box: Box<string> = {value = "hello"}

Flow Narrowing

The checker tracks types through control flow. After a nil check, the type is narrowed:

local function process(data: {value: number}?)
    if not data then return end
    print(data.value)  -- data is not nil here
end

Works with union types too:

type Exit = {kind: "exit", code: number}
type Message = {kind: "message", text: string}
type Event = Exit | Message

local function handle(e: Event)
    if e.kind == "exit" then
        print(e.code)  -- e is Exit
    else
        print(e.text)  -- e is Message
    end
end

Effects

Functions track side effects. The stdlib is annotated with what each function does — mutations, errors, I/O. This lets the checker understand code like:

assert(x ~= nil)
print(x.field)  -- x narrowed after assert

Running the VM

L := lua.NewState()
defer L.Close()

if err := L.DoString(`print("hello")`); err != nil {
    panic(err)
}

Error Metadata

WrapError and WrapErrorWithLua preserve metadata from wrapped *lua.Error values. For non-*lua.Error chains (for example errors from another package), register a process-wide metadata extractor once:

import (
    "errors"

    lua "github.com/wippyai/go-lua"
)

func init() {
    lua.ConfigureErrorMetadataExtractor(func(err error) *lua.ErrorMetadata {
        for e := err; e != nil; e = errors.Unwrap(e) {
            kindProvider, hasKind := e.(interface{ ErrorKind() string })
            retryProvider, hasRetry := e.(interface{ ErrorRetryable() (bool, bool) })
            detailsProvider, hasDetails := e.(interface{ ErrorDetails() map[string]any })
            if !hasKind && !hasRetry && !hasDetails {
                continue
            }

            meta := &lua.ErrorMetadata{}
            if hasKind {
                meta.Kind = lua.Kind(kindProvider.ErrorKind())
            }
            if hasRetry {
                if b, ok := retryProvider.ErrorRetryable(); ok {
                    v := b
                    meta.Retryable = &v
                }
            }
            if hasDetails {
                meta.Details = detailsProvider.ErrorDetails()
            }
            return meta
        }
        return nil
    })
}

ConfigureErrorMetadataExtractor is one-time (subsequent calls are ignored). For one-off calls, use WrapErrorWithMetadata(err, context, extractor) instead of changing global process state.

Type Checking

import (
    "github.com/ponyruntime/go-lua/compiler/parse"
    "github.com/ponyruntime/go-lua/types"
)

chunk, _ := parse.Parse(reader, "script.lua")
for _, d := range types.CheckChunk(chunk, types.WithStdlib()) {
    fmt.Printf("%s:%d: %s\n", d.Source, d.Line, d.Message)
}

License

MIT — see LICENSE. Based on gopher-lua by Yusuke Inuzuka.

About

Typesafe Lua for actors

Resources

License

Code of conduct

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •  

Languages