2
2
mirror of https://github.com/Hilbis/Hilbish synced 2025-07-01 08:42:04 +00:00
Hilbish/golibs/yarn/yarn.go

131 lines
2.4 KiB
Go

package yarn
import (
"fmt"
"hilbish/util"
"os"
"github.com/arnodel/golua/lib/packagelib"
rt "github.com/arnodel/golua/runtime"
)
var yarnMetaKey = rt.StringValue("hshyarn")
var globalSpool *Yarn
type Yarn struct {
initializer func(*rt.Runtime)
Loader packagelib.Loader
}
type Thread struct {
rtm *rt.Runtime
f rt.Callable
}
func New(init func(*rt.Runtime)) *Yarn {
yrn := &Yarn{
initializer: init,
}
yrn.Loader = packagelib.Loader{
Load: yrn.loaderFunc,
Name: "yarn",
}
globalSpool = yrn
return yrn
}
func (y *Yarn) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
yarnMeta := rt.NewTable()
yarnMeta.Set(rt.StringValue("__call"), rt.FunctionValue(rt.NewGoFunction(yarnrun, "__call", 1, true)))
rtm.SetRegistry(yarnMetaKey, rt.TableValue(yarnMeta))
exports := map[string]util.LuaExport{
"thread": {
Function: yarncreate,
ArgNum: 1,
Variadic: false,
},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
return rt.TableValue(mod), nil
}
func (y *Yarn) init(th *Thread) {
y.initializer(th.rtm)
}
// create(fun)
// Creates a new, fresh Yarn thread.
func yarncreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
fun, err := c.CallableArg(0)
if err != nil {
return nil, err
}
yrn := &Thread{
rtm: rt.New(os.Stdout),
f: fun,
}
globalSpool.init(yrn)
return c.PushingNext(t.Runtime, rt.UserDataValue(yarnUserData(t.Runtime, yrn))), nil
}
func yarnrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
yrn, err := yarnArg(c, 0)
if err != nil {
return nil, err
}
yrn.Run(c.Etc())
return c.Next(), nil
}
func (y *Thread) Run(args []rt.Value) {
go func() {
term := rt.NewTerminationWith(y.rtm.MainThread().CurrentCont(), 0, true)
err := rt.Call(y.rtm.MainThread(), rt.FunctionValue(y.f), args, term)
if err != nil {
panic(err)
}
}()
}
func yarnArg(c *rt.GoCont, arg int) (*Thread, error) {
j, ok := valueToYarn(c.Arg(arg))
if !ok {
return nil, fmt.Errorf("#%d must be a yarn thread", arg+1)
}
return j, nil
}
func valueToYarn(val rt.Value) (*Thread, bool) {
u, ok := val.TryUserData()
if !ok {
return nil, false
}
j, ok := u.Value().(*Thread)
return j, ok
}
func yarnUserData(rtm *rt.Runtime, t *Thread) *rt.UserData {
yarnMeta := rtm.Registry(yarnMetaKey)
return rt.NewUserData(t, yarnMeta.AsTable())
}