Skip to content

Instantly share code, notes, and snippets.

@amasad
Last active January 13, 2017 07:26
Show Gist options
  • Save amasad/3c93f6cc9f13a6478399c0ff5cadc6ce to your computer and use it in GitHub Desktop.
Save amasad/3c93f6cc9f13a6478399c0ff5cadc6ce to your computer and use it in GitHub Desktop.
A lua repl in go
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"regexp"
"strings"
"time"
"github.com/aarzilli/golua/lua"
)
type Message struct {
Command string `json:"command"`
Data string `json:"data"`
Error string `json:"error"`
}
func newMessage(cmd, data, error string) *Message {
return &Message{cmd, data, error}
}
// TODO: io.stdout, io.stderr are currently raw (print is redefined)
func main() {
encoder := json.NewEncoder(os.Stdout)
decoder := json.NewDecoder(os.Stdin)
var L *lua.State
defer L.Close()
init := func() {
L = lua.NewState()
L.OpenLibs()
// Golua renames those globals unsafe because "they can't call back
// to go" but we don't care about that. This works
L.GetGlobal("unsafe_pcall")
L.SetGlobal("pcall")
L.GetGlobal("unsafe_xpcall")
L.SetGlobal("xpcall")
print := func(L *lua.State) int {
nargs := L.GetTop()
formatted := make([]string, nargs)
for i := 0; i < nargs; i++ {
formatted = append([]string{format(L)}, formatted...)
}
encoder.Encode(newMessage("output", strings.Join(formatted, "\t")+"\n", ""))
return 0
}
L.Register("print", print)
}
init()
// Wait a bit to make sure the caller is reading our output
time.Sleep(200 * time.Millisecond)
encoder.Encode(newMessage("ready", "", ""))
for {
var msg Message
if err := decoder.Decode(&msg); err != nil {
log.Fatal("error while decoding message: ", err)
}
if msg.Command == "reset" {
L.Close()
init()
encoder.Encode(newMessage("ready", "", ""))
continue
}
if msg.Command != "eval" {
log.Fatal("unknown command ", msg.Command)
}
shouldReturn := regexp.MustCompile(`^=[^=]`)
code := msg.Data
if shouldReturn.MatchString(code) {
code = "return " + code[1:]
} else {
// Try to make it into a returnable expression
expr := "return " + code
if r := L.LoadString(expr); r == 0 {
code = expr
}
L.SetTop(-2)
}
if err := L.DoString(code); err != nil {
encoder.Encode(newMessage("result", "", err.Error()))
continue
}
ret := ""
if L.GetTop() > 0 {
ret = format(L)
}
encoder.Encode(newMessage("result", ret, ""))
}
}
func format(L *lua.State) string {
ret := ""
t := L.Type(-1)
switch t {
case -1:
fallthrough
case 0:
ret = "nil"
case 1:
r := L.ToBoolean(-1)
if r {
ret = "true"
} else {
ret = "false"
}
case 3:
ret = fmt.Sprintf("%g", L.ToNumber(-1))
case 4:
ret = L.ToString(-1)
default:
name := L.Typename(int(t))
address := L.ToPointer(-1)
hexAddress := fmt.Sprintf("%x", address)
ret = name + ": 0x" + hexAddress
}
L.SetTop(-2)
return ret
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment