2021-10-16 16:40:53 +00:00
|
|
|
package util
|
|
|
|
|
2022-04-04 10:40:02 +00:00
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"io"
|
2022-05-01 04:49:59 +00:00
|
|
|
"strings"
|
2022-04-04 10:40:02 +00:00
|
|
|
"os"
|
2022-05-01 04:49:59 +00:00
|
|
|
"os/user"
|
2022-04-04 10:40:02 +00:00
|
|
|
|
|
|
|
rt "github.com/arnodel/golua/runtime"
|
|
|
|
)
|
2021-10-16 16:40:53 +00:00
|
|
|
|
2021-11-22 23:59:28 +00:00
|
|
|
// Document adds a documentation string to a module.
|
|
|
|
// It is accessible via the __doc metatable.
|
2022-04-04 10:40:02 +00:00
|
|
|
func Document(module *rt.Table, doc string) {
|
|
|
|
mt := module.Metatable()
|
|
|
|
|
|
|
|
if mt == nil {
|
|
|
|
mt = rt.NewTable()
|
|
|
|
module.SetMetatable(mt)
|
2021-11-22 23:59:28 +00:00
|
|
|
}
|
2022-04-04 10:40:02 +00:00
|
|
|
|
|
|
|
mt.Set(rt.StringValue("__doc"), rt.StringValue(doc))
|
2021-11-22 23:59:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetField sets a field in a table, adding docs for it.
|
|
|
|
// It is accessible via the __docProp metatable. It is a table of the names of the fields.
|
2022-04-04 10:40:02 +00:00
|
|
|
func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) {
|
|
|
|
// TODO: ^ rtm isnt needed, i should remove it
|
2022-04-22 14:42:04 +00:00
|
|
|
SetFieldDoc(module, field, doc)
|
|
|
|
module.Set(rt.StringValue(field), value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetFieldDoc sets the __docProp metatable for a field on the
|
|
|
|
// module.
|
|
|
|
func SetFieldDoc(module *rt.Table, field, doc string) {
|
2022-04-04 10:40:02 +00:00
|
|
|
mt := module.Metatable()
|
2022-04-22 14:42:04 +00:00
|
|
|
|
2022-04-04 10:40:02 +00:00
|
|
|
if mt == nil {
|
|
|
|
mt = rt.NewTable()
|
|
|
|
module.SetMetatable(mt)
|
2021-11-23 00:19:36 +00:00
|
|
|
}
|
2022-04-22 14:42:04 +00:00
|
|
|
|
2022-04-04 10:40:02 +00:00
|
|
|
docProp := mt.Get(rt.StringValue("__docProp"))
|
2022-04-22 00:33:32 +00:00
|
|
|
if docProp == rt.NilValue {
|
|
|
|
docPropTbl := rt.NewTable()
|
|
|
|
mt.Set(rt.StringValue("__docProp"), rt.TableValue(docPropTbl))
|
|
|
|
docProp = mt.Get(rt.StringValue("__docProp"))
|
|
|
|
}
|
2021-10-16 16:40:53 +00:00
|
|
|
|
2022-04-04 10:40:02 +00:00
|
|
|
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc))
|
2022-04-22 14:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetFieldProtected sets a field in a protected table. A protected table
|
|
|
|
// is one which has a metatable proxy to ensure no overrides happen to it.
|
|
|
|
// It sets the field in the table and sets the __docProp metatable on the
|
|
|
|
// user facing table.
|
|
|
|
func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Value, doc string) {
|
|
|
|
SetFieldDoc(module, field, doc)
|
|
|
|
realModule.Set(rt.StringValue(field), value)
|
2021-10-16 16:40:53 +00:00
|
|
|
}
|
2021-11-23 00:19:36 +00:00
|
|
|
|
2022-04-04 10:40:02 +00:00
|
|
|
// DoString runs the code string in the Lua runtime.
|
|
|
|
func DoString(rtm *rt.Runtime, code string) error {
|
|
|
|
chunk, err := rtm.CompileAndLoadLuaChunk("<string>", []byte(code), rt.TableValue(rtm.GlobalEnv()))
|
|
|
|
if chunk != nil {
|
|
|
|
_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk))
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// DoFile runs the contents of the file in the Lua runtime.
|
|
|
|
func DoFile(rtm *rt.Runtime, path string) error {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
reader := bufio.NewReader(f)
|
|
|
|
c, err := reader.ReadByte()
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// unread so a char won't be missing
|
|
|
|
err = reader.UnreadByte()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf []byte
|
|
|
|
if c == byte('#') {
|
|
|
|
// shebang - skip that line
|
|
|
|
_, err := reader.ReadBytes('\n')
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
buf = []byte{'\n'}
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
line, err := reader.ReadBytes('\n')
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = append(buf, line...)
|
|
|
|
}
|
|
|
|
|
2022-04-20 01:25:52 +00:00
|
|
|
clos, err := rtm.LoadFromSourceOrCode(path, buf, "bt", rt.TableValue(rtm.GlobalEnv()), false)
|
|
|
|
if clos != nil {
|
|
|
|
_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(clos))
|
2022-04-04 10:40:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleStrCallback handles function parameters for Go functions which take
|
|
|
|
// a string and a closure.
|
|
|
|
func HandleStrCallback(t *rt.Thread, c *rt.GoCont) (string, *rt.Closure, error) {
|
|
|
|
if err := c.CheckNArgs(2); err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
name, err := c.StringArg(0)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
cb, err := c.ClosureArg(1)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return name, cb, err
|
|
|
|
}
|
2022-04-21 18:01:59 +00:00
|
|
|
|
|
|
|
// ForEach loops through a Lua table.
|
|
|
|
func ForEach(tbl *rt.Table, cb func(key rt.Value, val rt.Value)) {
|
|
|
|
nextVal := rt.NilValue
|
|
|
|
for {
|
|
|
|
key, val, _ := tbl.Next(nextVal)
|
|
|
|
if key == rt.NilValue {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
nextVal = key
|
|
|
|
|
|
|
|
cb(key, val)
|
|
|
|
}
|
|
|
|
}
|
2022-05-01 04:49:59 +00:00
|
|
|
|
2022-05-01 11:20:40 +00:00
|
|
|
// ExpandHome expands ~ (tilde) in the path, changing it to the user home
|
|
|
|
// directory.
|
2022-05-01 04:49:59 +00:00
|
|
|
func ExpandHome(path string) string {
|
2022-05-01 11:20:40 +00:00
|
|
|
if strings.HasPrefix(path, "~") {
|
|
|
|
curuser, _ := user.Current()
|
|
|
|
homedir := curuser.HomeDir
|
2022-05-01 04:49:59 +00:00
|
|
|
|
2022-05-01 11:20:40 +00:00
|
|
|
return strings.Replace(path, "~", homedir, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return path
|
2022-05-01 04:49:59 +00:00
|
|
|
}
|
2022-05-01 11:20:40 +00:00
|
|
|
|