From 4524c1451ade4d17e24023f5ead70f87a645a368 Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 16:54:15 -0400 Subject: [PATCH 01/24] feat: add moonlight lua abstraction library future plans: add the ability to use c lua (or luajit) with hilbish benefits? speed, i guess? --- aliases.go | 2 +- api.go | 176 ++++++++++++++++++------------------ complete.go | 27 ++++-- exec.go | 16 ++-- history.go | 6 +- job.go | 17 +++- lua.go | 38 ++++---- main.go | 7 +- moonlight/export_golua.go | 13 +++ moonlight/function.go | 3 + moonlight/function_golua.go | 29 ++++++ moonlight/loader_golua.go | 22 +++++ moonlight/runtime_golua.go | 35 +++++++ moonlight/table_golua.go | 29 ++++++ moonlight/util_golua.go | 71 +++++++++++++++ moonlight/value_golua.go | 23 +++++ os.go | 13 +-- rl.go | 12 +-- runnermode.go | 2 + sink.go | 20 ++-- timer.go | 2 +- timerhandler.go | 29 +++--- userdir.go | 11 ++- util/export.go | 4 +- util/util.go | 76 +--------------- 25 files changed, 434 insertions(+), 249 deletions(-) create mode 100644 moonlight/export_golua.go create mode 100644 moonlight/function.go create mode 100644 moonlight/function_golua.go create mode 100644 moonlight/loader_golua.go create mode 100644 moonlight/runtime_golua.go create mode 100644 moonlight/table_golua.go create mode 100644 moonlight/util_golua.go create mode 100644 moonlight/value_golua.go diff --git a/aliases.go b/aliases.go index 8c90fe5..4b0eb8d 100644 --- a/aliases.go +++ b/aliases.go @@ -97,7 +97,7 @@ func (a *aliasModule) Resolve(cmdstr string) string { func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table { // create a lua module with our functions hshaliasesLua := map[string]util.LuaExport{ - "add": util.LuaExport{hlalias, 2, false}, + //"add": util.LuaExport{hlalias, 2, false}, "list": util.LuaExport{a.luaList, 0, false}, "del": util.LuaExport{a.luaDelete, 1, false}, "resolve": util.LuaExport{a.luaResolve, 1, false}, diff --git a/api.go b/api.go index 43e361a..c3081a2 100644 --- a/api.go +++ b/api.go @@ -15,56 +15,52 @@ package main import ( "bytes" "errors" - "fmt" + //"fmt" "io" "os" - "os/exec" + //"os/exec" "runtime" "strings" - "syscall" - "time" + //"syscall" + //"time" - "hilbish/util" + //"hilbish/util" + "hilbish/moonlight" rt "github.com/arnodel/golua/runtime" - "github.com/arnodel/golua/lib/packagelib" + //"github.com/arnodel/golua/lib/packagelib" "github.com/arnodel/golua/lib/iolib" "github.com/maxlandon/readline" "mvdan.cc/sh/v3/interp" ) -var exports = map[string]util.LuaExport{ - "alias": {hlalias, 2, false}, - "appendPath": {hlappendPath, 1, false}, - "complete": {hlcomplete, 2, false}, - "cwd": {hlcwd, 0, false}, - "exec": {hlexec, 1, false}, - "runnerMode": {hlrunnerMode, 1, false}, - "goro": {hlgoro, 1, true}, - "highlighter": {hlhighlighter, 1, false}, - "hinter": {hlhinter, 1, false}, - "multiprompt": {hlmultiprompt, 1, false}, - "prependPath": {hlprependPath, 1, false}, - "prompt": {hlprompt, 1, true}, - "inputMode": {hlinputMode, 1, false}, - "interval": {hlinterval, 2, false}, - "read": {hlread, 1, false}, - "run": {hlrun, 1, true}, - "timeout": {hltimeout, 2, false}, - "which": {hlwhich, 1, false}, -} +var hshMod *moonlight.Table -var hshMod *rt.Table -var hilbishLoader = packagelib.Loader{ - Load: hilbishLoad, - Name: "hilbish", -} - -func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { - mod := rt.NewTable() - - util.SetExports(rtm, mod, exports) - hshMod = mod +func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { + var exports = map[string]moonlight.Export{ + "alias": {hlalias, 2, false}, + /* + "appendPath": {hlappendPath, 1, false}, + "complete": {hlcomplete, 2, false}, + "cwd": {hlcwd, 0, false}, + "exec": {hlexec, 1, false}, + "runnerMode": {hlrunnerMode, 1, false}, + "goro": {hlgoro, 1, true}, + "highlighter": {hlhighlighter, 1, false}, + "hinter": {hlhinter, 1, false}, + "multiprompt": {hlmultiprompt, 1, false}, + "prependPath": {hlprependPath, 1, false}, + "prompt": {hlprompt, 1, true}, + "inputMode": {hlinputMode, 1, false}, + "interval": {hlinterval, 2, false}, + "read": {hlread, 1, false}, + "run": {hlrun, 1, true}, + "timeout": {hltimeout, 2, false}, + "which": {hlwhich, 1, false}, + */ + } + hshMod = moonlight.NewTable() + mlr.SetExports(hshMod, exports) host, _ := os.Hostname() username := curuser.Username @@ -73,68 +69,68 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows } - util.SetField(rtm, mod, "ver", rt.StringValue(getVersion())) - util.SetField(rtm, mod, "goVersion", rt.StringValue(runtime.Version())) - util.SetField(rtm, mod, "user", rt.StringValue(username)) - util.SetField(rtm, mod, "host", rt.StringValue(host)) - util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir)) - util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir)) - util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive)) - util.SetField(rtm, mod, "login", rt.BoolValue(login)) - util.SetField(rtm, mod, "vimMode", rt.NilValue) - util.SetField(rtm, mod, "exitCode", rt.IntValue(0)) + hshMod.SetField("ver", moonlight.StringValue(getVersion())) + hshMod.SetField("goVersion", moonlight.StringValue(runtime.Version())) + hshMod.SetField("user", moonlight.StringValue(username)) + hshMod.SetField("host", moonlight.StringValue(host)) + hshMod.SetField("home", moonlight.StringValue(curuser.HomeDir)) + hshMod.SetField("dataDir", moonlight.StringValue(dataDir)) + hshMod.SetField("interactive", moonlight.BoolValue(interactive)) + hshMod.SetField("login", moonlight.BoolValue(login)) + hshMod.SetField("exitCode", moonlight.IntValue(0)) + //util.SetField(rtm, mod, "vimMode", rt.NilValue) // hilbish.userDir table - hshuser := userDirLoader(rtm) - mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) + //hshuser := userDirLoader(rtm) + //mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) // hilbish.os table - hshos := hshosLoader(rtm) - mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) + //hshos := hshosLoader(rtm) + //mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) // hilbish.aliases table - aliases = newAliases() - aliasesModule := aliases.Loader(rtm) - mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) + //aliases = newAliases() + //aliasesModule := aliases.Loader(rtm) + //mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) // hilbish.history table - historyModule := lr.Loader(rtm) - mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) + //historyModule := lr.Loader(rtm) + //mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) // hilbish.completion table - hshcomp := completionLoader(rtm) + //hshcomp := completionLoader(rtm) // TODO: REMOVE "completion" AND ONLY USE "completions" WITH AN S - mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) - mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp)) + //mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) + //mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp)) // hilbish.runner table - runnerModule := runnerModeLoader(rtm) - mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) + //runnerModule := runnerModeLoader(rtm) + //mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) // hilbish.jobs table - jobs = newJobHandler() - jobModule := jobs.loader(rtm) - mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) + //jobs = newJobHandler() + //jobModule := jobs.loader(rtm) + //mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) // hilbish.timers table - timers = newTimersModule() - timersModule := timers.loader(rtm) - mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule)) + //timers = newTimersModule() + //timersModule := timers.loader(rtm) + //mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule)) - editorModule := editorLoader(rtm) - mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule)) + //editorModule := editorLoader(rtm) + //mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule)) - versionModule := rt.NewTable() - util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch)) - util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion())) - util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit)) - util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName)) - mod.Set(rt.StringValue("version"), rt.TableValue(versionModule)) + //versionModule := rt.NewTable() + //util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch)) + //util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion())) + //util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit)) + //util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName)) + //mod.Set(rt.StringValue("version"), rt.TableValue(versionModule)) - pluginModule := moduleLoader(rtm) - mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule)) + //pluginModule := moduleLoader(rtm) + //mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule)) - return rt.TableValue(mod), nil + return moonlight.TableValue(hshMod) } func getenv(key, fallback string) string { @@ -146,12 +142,12 @@ func getenv(key, fallback string) string { } func setVimMode(mode string) { - util.SetField(l, hshMod, "vimMode", rt.StringValue(mode)) + hshMod.SetField("vimMode", rt.StringValue(mode)) hooks.Emit("hilbish.vimMode", mode) } func unsetVimMode() { - util.SetField(l, hshMod, "vimMode", rt.NilValue) + hshMod.SetField("vimMode", rt.NilValue) } func handleStream(v rt.Value, strms *streams, errStream bool) error { @@ -429,15 +425,17 @@ hilbish.alias('dircount', 'ls %1 | wc -l') -- "dircount ~" would count how many files are in ~ (home directory). #example */ -func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.CheckNArgs(2); err != nil { +//func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +func hlalias(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.CheckNArgs(c, 2); err != nil { return nil, err } - cmd, err := c.StringArg(0) + + cmd, err := mlr.StringArg(c, 0) if err != nil { return nil, err } - orig, err := c.StringArg(1) + orig, err := mlr.StringArg(c, 1) if err != nil { return nil, err } @@ -461,7 +459,6 @@ hilbish.appendPath { '~/.local/bin' } #example -*/ func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -529,7 +526,9 @@ func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +*/ +/* // goro(fn) // Puts `fn` in a Goroutine. // This can be used to run any function in another thread at the same time as other Lua code. @@ -613,6 +612,7 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil } +*/ // complete(scope, cb) // Registers a completion handler for the specified scope. @@ -648,7 +648,6 @@ hilbish.complete('command.sudo', function(query, ctx, fields) return {compGroup}, pfx end) #example -*/ func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { scope, cb, err := util.HandleStrCallback(t, c) if err != nil { @@ -658,7 +657,9 @@ func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +*/ +/* // prependPath(dir) // Prepends `dir` to $PATH. // #param dir string @@ -769,6 +770,7 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +*/ // hinter(line, pos) // The command line hint handler. It gets called on every key insert to @@ -786,6 +788,7 @@ function hilbish.hinter(line, pos) end #example */ +/* func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } @@ -815,3 +818,4 @@ func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.StringValue(line)), nil } +*/ diff --git a/complete.go b/complete.go index 1c40b20..45a932f 100644 --- a/complete.go +++ b/complete.go @@ -1,14 +1,14 @@ package main import ( - "errors" + //"errors" "path/filepath" "strings" "os" "hilbish/util" - rt "github.com/arnodel/golua/runtime" + //rt "github.com/arnodel/golua/runtime" ) var charEscapeMap = []string{ @@ -191,6 +191,7 @@ func escapeFilename(fname string) string { // #interface completion // tab completions // The completions interface deals with tab completions. +/* func completionLoader(rtm *rt.Runtime) *rt.Table { exports := map[string]util.LuaExport{ "bins": {hcmpBins, 3, false}, @@ -204,6 +205,7 @@ func completionLoader(rtm *rt.Runtime) *rt.Table { return mod } +*/ // #interface completion // bins(query, ctx, fields) -> entries (table), prefix (string) @@ -231,6 +233,7 @@ hilbish.complete('command.sudo', function(query, ctx, fields) end) #example */ +/* func hcmpBins(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { query, ctx, fds, err := getCompleteParams(t, c) if err != nil { @@ -246,6 +249,7 @@ func hcmpBins(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil } +*/ // #interface completion // call(name, query, ctx, fields) -> completionGroups (table), prefix (string) @@ -256,6 +260,7 @@ func hcmpBins(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #param query string // #param ctx string // #param fields table +/* func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.CheckNArgs(4); err != nil { return nil, err @@ -283,11 +288,15 @@ func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, errors.New("completer " + completer + " does not exist") } - // we must keep the holy 80 cols cont := c.Next() - err = rt.Call(l.MainThread(), rt.FunctionValue(completecb), - []rt.Value{rt.StringValue(query), rt.StringValue(ctx), rt.TableValue(fields)}, - cont) + err = l.Call(moonlight.FunctionValue(completecb), []moonlight.Value{ + moonlight.StringValue(query), + moonlight.StringValue(ctx), + moonlight.TableValue(fields) + }, cont) + err = rt.Call(l.MainThread(), rt.FunctionValue(completecb), []rt.Value{ + rt.StringValue(query), rt.StringValue(ctx), rt.TableValue(fields) + }, cont) if err != nil { return nil, err @@ -295,6 +304,7 @@ func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return cont, nil } +*/ // #interface completion // files(query, ctx, fields) -> entries (table), prefix (string) @@ -303,6 +313,7 @@ func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #param query string // #param ctx string // #param fields table +/* func hcmpFiles(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { query, ctx, fds, err := getCompleteParams(t, c) if err != nil { @@ -318,6 +329,7 @@ func hcmpFiles(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil } +*/ // #interface completion // handler(line, pos) @@ -340,11 +352,11 @@ function hilbish.completion.handler(line, pos) end #example */ +/* func hcmpHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } - func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) { if err := c.CheckNArgs(3); err != nil { return "", "", []string{}, err @@ -371,3 +383,4 @@ func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, er return query, ctx, fds, err } +*/ diff --git a/exec.go b/exec.go index cf1b299..9836a6c 100644 --- a/exec.go +++ b/exec.go @@ -14,7 +14,8 @@ import ( "syscall" "time" - "hilbish/util" + "hilbish/moonlight" + //"hilbish/util" rt "github.com/arnodel/golua/runtime" "mvdan.cc/sh/v3/shell" @@ -170,15 +171,13 @@ func reprompt(input string) (string, error) { } func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8, continued bool, runnerErr, err error) { - term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false) - err = rt.Call(l.MainThread(), runr, []rt.Value{rt.StringValue(userInput)}, term) + runnerRet, err := l.Call1(runr, moonlight.StringValue(userInput)) if err != nil { return "", 124, false, nil, err } var runner *rt.Table var ok bool - runnerRet := term.Get(0) if runner, ok = runnerRet.TryTable(); !ok { fmt.Fprintln(os.Stderr, "runner did not return a table") exitCode = 125 @@ -207,7 +206,8 @@ func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8 func handleLua(input string) (string, uint8, error) { cmdString := aliases.Resolve(input) // First try to load input, essentially compiling to bytecode - chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv())) + rtm := l.UnderlyingRuntime() + chunk, err := rtm.CompileAndLoadLuaChunk("", []byte(cmdString), moonlight.TableValue(l.GlobalTable())) if err != nil && noexecute { fmt.Println(err) /* if lerr, ok := err.(*lua.ApiError); ok { @@ -221,7 +221,7 @@ func handleLua(input string) (string, uint8, error) { // And if there's no syntax errors and -n isnt provided, run if !noexecute { if chunk != nil { - _, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk)) + _, err = l.Call1(rt.FunctionValue(chunk)) } } if err == nil { @@ -353,7 +353,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc { sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud)) sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud)) - luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(cmd), rt.TableValue(luacmdArgs), rt.TableValue(sinks)) + luaexitcode, err := l.Call1(rt.FunctionValue(cmd), rt.TableValue(luacmdArgs), rt.TableValue(sinks)) if err != nil { fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error()) return interp.NewExitStatus(1) @@ -580,7 +580,7 @@ func splitInput(input string) ([]string, string) { } func cmdFinish(code uint8, cmdstr string, private bool) { - util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code))) + hshMod.SetField("exitCode", rt.IntValue(int64(code))) // using AsValue (to convert to lua type) on an interface which is an int // results in it being unknown in lua .... ???? // so we allow the hook handler to take lua runtime Values diff --git a/history.go b/history.go index 51ccf27..aab4466 100644 --- a/history.go +++ b/history.go @@ -14,7 +14,7 @@ type luaHistory struct {} func (h *luaHistory) Write(line string) (int, error) { histWrite := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("add")) - ln, err := rt.Call1(l.MainThread(), histWrite, rt.StringValue(line)) + ln, err := l.Call1(histWrite, rt.StringValue(line)) var num int64 if ln.Type() == rt.IntType { @@ -26,7 +26,7 @@ func (h *luaHistory) Write(line string) (int, error) { func (h *luaHistory) GetLine(idx int) (string, error) { histGet := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("get")) - lcmd, err := rt.Call1(l.MainThread(), histGet, rt.IntValue(int64(idx))) + lcmd, err := l.Call1(histGet, rt.IntValue(int64(idx))) var cmd string if lcmd.Type() == rt.StringType { @@ -38,7 +38,7 @@ func (h *luaHistory) GetLine(idx int) (string, error) { func (h *luaHistory) Len() int { histSize := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("size")) - ln, _ := rt.Call1(l.MainThread(), histSize) + ln, _ := l.Call1(histSize) var num int64 if ln.Type() == rt.IntType { diff --git a/job.go b/job.go index f5bd6f2..cf2615f 100644 --- a/job.go +++ b/job.go @@ -10,6 +10,7 @@ import ( "sync" "syscall" + "hilbish/moonlight" "hilbish/util" rt "github.com/arnodel/golua/runtime" @@ -310,7 +311,8 @@ Manage interactive jobs in Hilbish via Lua. Jobs are the name of background tasks/commands. A job can be started via interactive usage or with the functions defined below for use in external runners. */ -func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { +func (j *jobHandler) loader() *moonlight.Table { + /* jobMethods := rt.NewTable() jFuncs := map[string]util.LuaExport{ "stop": {luaStopJob, 1, false}, @@ -319,7 +321,9 @@ func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { "background": {luaBackgroundJob, 1, false}, } util.SetExports(l, jobMethods, jFuncs) + */ +/* jobMeta := rt.NewTable() jobIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { j, _ := jobArg(c, 0) @@ -348,17 +352,20 @@ func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { jobMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(jobIndex, "__index", 2, false))) l.SetRegistry(jobMetaKey, rt.TableValue(jobMeta)) +*/ - jobFuncs := map[string]util.LuaExport{ + jobFuncs := map[string]moonlight.Export{ + /* "all": {j.luaAllJobs, 0, false}, "last": {j.luaLastJob, 0, false}, "get": {j.luaGetJob, 1, false}, "add": {j.luaAddJob, 3, false}, "disown": {j.luaDisownJob, 1, false}, + */ } - luaJob := rt.NewTable() - util.SetExports(rtm, luaJob, jobFuncs) + luaJob := moonlight.NewTable() + l.SetExports(luaJob, jobFuncs) return luaJob } @@ -383,7 +390,7 @@ func valueToJob(val rt.Value) (*job, bool) { } func jobUserData(j *job) *rt.UserData { - jobMeta := l.Registry(jobMetaKey) + jobMeta := l.UnderlyingRuntime().Registry(jobMetaKey) return rt.NewUserData(j, jobMeta.AsTable()) } diff --git a/lua.go b/lua.go index 94b7910..161fa38 100644 --- a/lua.go +++ b/lua.go @@ -4,32 +4,27 @@ import ( "fmt" "os" - "hilbish/util" - "hilbish/golibs/bait" - "hilbish/golibs/commander" - "hilbish/golibs/fs" - "hilbish/golibs/terminal" + //"hilbish/util" + //"hilbish/golibs/bait" + //"hilbish/golibs/commander" + //"hilbish/golibs/fs" + //"hilbish/golibs/terminal" - rt "github.com/arnodel/golua/runtime" - "github.com/arnodel/golua/lib" - "github.com/arnodel/golua/lib/debuglib" + "hilbish/moonlight" ) var minimalconf = `hilbish.prompt '& '` func luaInit() { - l = rt.New(os.Stdout) - l.PushContext(rt.RuntimeContextDef{ - MessageHandler: debuglib.Traceback, - }) - lib.LoadAll(l) - setupSinkType(l) + l = moonlight.NewRuntime() + setupSinkType() - lib.LoadLibs(l, hilbishLoader) + l.LoadLibrary(hilbishLoader, "hilbish") // yes this is stupid, i know - util.DoString(l, "hilbish = require 'hilbish'") + l.DoString("hilbish = require 'hilbish'") // Add fs and terminal module module to Lua + /* lib.LoadLibs(l, fs.Loader) lib.LoadLibs(l, terminal.Loader) @@ -54,16 +49,17 @@ func luaInit() { lr.rl.RawInputCallback = func(r []rune) { hooks.Emit("hilbish.rawInput", string(r)) } + */ // Add more paths that Lua can require from - _, err := util.DoString(l, "package.path = package.path .. " + requirePaths) + _, err := l.DoString("package.path = package.path .. " + requirePaths) if err != nil { fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.") } - err1 := util.DoFile(l, "nature/init.lua") + err1 := l.DoFile("nature/init.lua") if err1 != nil { - err2 := util.DoFile(l, preloadPath) + err2 := l.DoFile(preloadPath) if err2 != nil { fmt.Fprintln(os.Stderr, "Missing nature module, some functionality and builtins will be missing.") fmt.Fprintln(os.Stderr, "local error:", err1) @@ -76,9 +72,9 @@ func runConfig(confpath string) { if !interactive { return } - err := util.DoFile(l, confpath) + err := l.DoFile(confpath) if err != nil { fmt.Fprintln(os.Stderr, err, "\nAn error has occured while loading your config! Falling back to minimal default config.") - util.DoString(l, minimalconf) + l.DoString(minimalconf) } } diff --git a/main.go b/main.go index fd511a9..7a0c7b3 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "hilbish/util" "hilbish/golibs/bait" "hilbish/golibs/commander" + "hilbish/moonlight" rt "github.com/arnodel/golua/runtime" "github.com/pborman/getopt" @@ -24,7 +25,7 @@ import ( ) var ( - l *rt.Runtime + l *moonlight.Runtime lr *lineReader luaCompletions = map[string]*rt.Closure{} @@ -171,8 +172,8 @@ func main() { luaArgs.Set(rt.IntValue(int64(i)), rt.StringValue(arg)) } - l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs)) - err := util.DoFile(l, getopt.Arg(0)) + l.GlobalTable().SetField("args", rt.TableValue(luaArgs)) + err := l.DoFile(getopt.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, err) exit(1) diff --git a/moonlight/export_golua.go b/moonlight/export_golua.go new file mode 100644 index 0000000..426d6a8 --- /dev/null +++ b/moonlight/export_golua.go @@ -0,0 +1,13 @@ +package moonlight + +type Export struct{ + Function GoToLuaFunc + ArgNum int + Variadic bool +} + +func (mlr *Runtime) SetExports(tbl *Table, exports map[string]Export) { + for name, export := range exports { + mlr.rt.SetEnvGoFunc(tbl.lt, name, mlr.GoFunction(export.Function), export.ArgNum, export.Variadic) + } +} diff --git a/moonlight/function.go b/moonlight/function.go new file mode 100644 index 0000000..3ebdff5 --- /dev/null +++ b/moonlight/function.go @@ -0,0 +1,3 @@ +package moonlight + +type GoToLuaFunc func(mlr *Runtime, c *GoCont) (Cont, error) diff --git a/moonlight/function_golua.go b/moonlight/function_golua.go new file mode 100644 index 0000000..ad019ff --- /dev/null +++ b/moonlight/function_golua.go @@ -0,0 +1,29 @@ +package moonlight + +import ( + rt "github.com/arnodel/golua/runtime" +) + +type GoFunctionFunc = rt.GoFunctionFunc + +type GoCont = rt.GoCont +type Cont = rt.Cont + +func (mlr *Runtime) CheckNArgs(c *GoCont, num int) error { + return c.CheckNArgs(num) +} + +func (mlr *Runtime) StringArg(c *GoCont, num int) (string, error) { + return c.StringArg(num) +} + +func (mlr *Runtime) GoFunction(fun GoToLuaFunc) rt.GoFunctionFunc { + return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + gocont := GoCont(*c) + return fun(mlr, &gocont) + } +} + +func (mlr *Runtime) Call1(val Value, args ...Value) (Value, error) { + return rt.Call1(mlr.rt.MainThread(), val, args...) +} diff --git a/moonlight/loader_golua.go b/moonlight/loader_golua.go new file mode 100644 index 0000000..e2d505f --- /dev/null +++ b/moonlight/loader_golua.go @@ -0,0 +1,22 @@ +package moonlight + +import ( + rt "github.com/arnodel/golua/runtime" + "github.com/arnodel/golua/lib" + "github.com/arnodel/golua/lib/packagelib" +) + +type Loader func(*Runtime) Value + +func (mlr *Runtime) LoadLibrary(ldr Loader, name string) { + goluaLoader := packagelib.Loader{ + Load: func(rt *rt.Runtime) (rt.Value, func()) { + val := ldr(specificRuntimeToGeneric(rt)) + + return val, nil + }, + Name: name, + } + + lib.LoadLibs(mlr.rt, goluaLoader) +} diff --git a/moonlight/runtime_golua.go b/moonlight/runtime_golua.go new file mode 100644 index 0000000..6072ea4 --- /dev/null +++ b/moonlight/runtime_golua.go @@ -0,0 +1,35 @@ +package moonlight + +import ( + "os" + + rt "github.com/arnodel/golua/runtime" + "github.com/arnodel/golua/lib" + "github.com/arnodel/golua/lib/debuglib" +) + +type Runtime struct{ + rt *rt.Runtime +} + +func NewRuntime() *Runtime { + r := rt.New(os.Stdout) + r.PushContext(rt.RuntimeContextDef{ + MessageHandler: debuglib.Traceback, + }) + lib.LoadAll(r) + + return specificRuntimeToGeneric(r) +} + +func specificRuntimeToGeneric(rtm *rt.Runtime) *Runtime { + rr := Runtime{ + rt: rtm, + } + + return &rr +} + +func (mlr *Runtime) UnderlyingRuntime() *rt.Runtime { + return mlr.rt +} diff --git a/moonlight/table_golua.go b/moonlight/table_golua.go new file mode 100644 index 0000000..3c64eb9 --- /dev/null +++ b/moonlight/table_golua.go @@ -0,0 +1,29 @@ +package moonlight + +import ( + rt "github.com/arnodel/golua/runtime" +) + +type Table struct{ + lt *rt.Table +} + +func NewTable() *Table { + return &Table{ + lt: rt.NewTable(), + } +} + +func (t *Table) Get(val Value) Value { + return t.lt.Get(val) +} + +func (t *Table) SetField(key string, value Value) { + t.lt.Set(rt.StringValue(key), value) +} + +func (mlr *Runtime) GlobalTable() *Table { + return &Table{ + lt: mlr.rt.GlobalEnv(), + } +} diff --git a/moonlight/util_golua.go b/moonlight/util_golua.go new file mode 100644 index 0000000..bc0a440 --- /dev/null +++ b/moonlight/util_golua.go @@ -0,0 +1,71 @@ +package moonlight + +import ( + "bufio" + "io" + "os" + + rt "github.com/arnodel/golua/runtime" +) + +// DoString runs the code string in the Lua runtime. +func (mlr *Runtime) DoString(code string) (rt.Value, error) { + chunk, err := mlr.rt.CompileAndLoadLuaChunk("", []byte(code), rt.TableValue(mlr.rt.GlobalEnv())) + var ret rt.Value + if chunk != nil { + ret, err = rt.Call1(mlr.rt.MainThread(), rt.FunctionValue(chunk)) + } + + return ret, err +} + +// DoFile runs the contents of the file in the Lua runtime. +func (mlr *Runtime) DoFile(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...) + } + + clos, err := mlr.rt.LoadFromSourceOrCode(path, buf, "bt", rt.TableValue(mlr.rt.GlobalEnv()), false) + if clos != nil { + _, err = rt.Call1(mlr.rt.MainThread(), rt.FunctionValue(clos)) + } + + return err +} diff --git a/moonlight/value_golua.go b/moonlight/value_golua.go new file mode 100644 index 0000000..2300da4 --- /dev/null +++ b/moonlight/value_golua.go @@ -0,0 +1,23 @@ +package moonlight + +import ( + rt "github.com/arnodel/golua/runtime" +) + +type Value = rt.Value + +func StringValue(str string) Value { + return rt.StringValue(str) +} + +func IntValue(i int) Value { + return rt.IntValue(int64(i)) +} + +func BoolValue(b bool) Value { + return rt.BoolValue(b) +} + +func TableValue(t *Table) Value { + return rt.TableValue(t.lt) +} diff --git a/os.go b/os.go index 46e3d3c..4738d9b 100644 --- a/os.go +++ b/os.go @@ -1,7 +1,8 @@ package main import ( - "hilbish/util" + "hilbish/moonlight" + //"hilbish/util" rt "github.com/arnodel/golua/runtime" "github.com/blackfireio/osinfo" @@ -14,13 +15,13 @@ import ( // #field family Family name of the current OS // #field name Pretty name of the current OS // #field version Version of the current OS -func hshosLoader(rtm *rt.Runtime) *rt.Table { +func hshosLoader() *moonlight.Table { info, _ := osinfo.GetOSInfo() - mod := rt.NewTable() + mod := moonlight.NewTable() - util.SetField(rtm, mod, "family", rt.StringValue(info.Family)) - util.SetField(rtm, mod, "name", rt.StringValue(info.Name)) - util.SetField(rtm, mod, "version", rt.StringValue(info.Version)) + mod.SetField("family", rt.StringValue(info.Family)) + mod.SetField("name", rt.StringValue(info.Name)) + mod.SetField("version", rt.StringValue(info.Version)) return mod } diff --git a/rl.go b/rl.go index 231d04b..28db1f6 100644 --- a/rl.go +++ b/rl.go @@ -27,7 +27,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { regexSearcher := rl.Searcher rl.Searcher = func(needle string, haystack []string) []string { - fz, _ := util.DoString(l, "return hilbish.opts.fuzzy") + fz, _ := l.DoString("return hilbish.opts.fuzzy") fuzz, ok := fz.TryBool() if !fuzz || !ok { return regexSearcher(needle, haystack) @@ -71,8 +71,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { } rl.HintText = func(line []rune, pos int) []rune { hinter := hshMod.Get(rt.StringValue("hinter")) - retVal, err := rt.Call1(l.MainThread(), hinter, - rt.StringValue(string(line)), rt.IntValue(int64(pos))) + retVal, err := l.Call1(hinter, rt.StringValue(string(line)), rt.IntValue(int64(pos))) if err != nil { fmt.Println(err) return []rune{} @@ -87,8 +86,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { } rl.SyntaxHighlighter = func(line []rune) string { highlighter := hshMod.Get(rt.StringValue("highlighter")) - retVal, err := rt.Call1(l.MainThread(), highlighter, - rt.StringValue(string(line))) + retVal, err := l.Call1(highlighter, rt.StringValue(string(line))) if err != nil { fmt.Println(err) return string(line) @@ -102,9 +100,9 @@ func newLineReader(prompt string, noHist bool) *lineReader { return highlighted } rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) { - term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false) + term := rt.NewTerminationWith(l.UnderlyingRuntime().MainThread().CurrentCont(), 2, false) compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler")) - err := rt.Call(l.MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)), + err := rt.Call(l.UnderlyingRuntime().MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)), rt.IntValue(int64(pos))}, term) var compGroups []*readline.CompletionGroup diff --git a/runnermode.go b/runnermode.go index 55adfdc..e66e5fa 100644 --- a/runnermode.go +++ b/runnermode.go @@ -51,9 +51,11 @@ end) */ func runnerModeLoader(rtm *rt.Runtime) *rt.Table { exports := map[string]util.LuaExport{ + /* "sh": {shRunner, 1, false}, "lua": {luaRunner, 1, false}, "setMode": {hlrunnerMode, 1, false}, + */ } mod := rt.NewTable() diff --git a/sink.go b/sink.go index 3aa5507..5934533 100644 --- a/sink.go +++ b/sink.go @@ -7,7 +7,8 @@ import ( "os" "strings" - "hilbish/util" + //"hilbish/util" + "hilbish/moonlight" rt "github.com/arnodel/golua/runtime" ) @@ -25,20 +26,22 @@ type sink struct{ autoFlush bool } -func setupSinkType(rtm *rt.Runtime) { - sinkMeta := rt.NewTable() +func setupSinkType() { + //sinkMeta := moonlight.NewTable() - sinkMethods := rt.NewTable() - sinkFuncs := map[string]util.LuaExport{ + sinkMethods := moonlight.NewTable() + sinkFuncs := map[string]moonlight.Export{ + /* "flush": {luaSinkFlush, 1, false}, "read": {luaSinkRead, 1, false}, "readAll": {luaSinkReadAll, 1, false}, "autoFlush": {luaSinkAutoFlush, 2, false}, "write": {luaSinkWrite, 2, false}, "writeln": {luaSinkWriteln, 2, false}, + */ } - util.SetExports(l, sinkMethods, sinkFuncs) - + l.SetExports(sinkMethods, sinkFuncs) +/* sinkIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { s, _ := sinkArg(c, 0) @@ -65,6 +68,7 @@ func setupSinkType(rtm *rt.Runtime) { sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false))) l.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta)) +*/ } @@ -255,6 +259,6 @@ func valueToSink(val rt.Value) (*sink, bool) { } func sinkUserData(s *sink) *rt.UserData { - sinkMeta := l.Registry(sinkMetaKey) + sinkMeta := l.UnderlyingRuntime().Registry(sinkMetaKey) return rt.NewUserData(s, sinkMeta.AsTable()) } diff --git a/timer.go b/timer.go index 5d536f5..5c1fa0d 100644 --- a/timer.go +++ b/timer.go @@ -47,7 +47,7 @@ func (t *timer) start() error { for { select { case <-t.ticker.C: - _, err := rt.Call1(l.MainThread(), rt.FunctionValue(t.fun)) + _, err := l.Call1(rt.FunctionValue(t.fun)) if err != nil { fmt.Fprintln(os.Stderr, "Error in function:\n", err) t.stop() diff --git a/timerhandler.go b/timerhandler.go index 0a8e34f..c4cfc61 100644 --- a/timerhandler.go +++ b/timerhandler.go @@ -5,7 +5,8 @@ import ( "sync" "time" - "hilbish/util" + "hilbish/moonlight" + //"hilbish/util" rt "github.com/arnodel/golua/runtime" ) @@ -133,13 +134,15 @@ t:start() print(t.running) // true ``` */ -func (th *timersModule) loader(rtm *rt.Runtime) *rt.Table { - timerMethods := rt.NewTable() - timerFuncs := map[string]util.LuaExport{ +func (th *timersModule) loader() *moonlight.Table { + timerMethods := moonlight.NewTable() + timerFuncs := map[string]moonlight.Export{ + /* "start": {timerStart, 1, false}, "stop": {timerStop, 1, false}, + */ } - util.SetExports(rtm, timerMethods, timerFuncs) + l.SetExports(timerMethods, timerFuncs) timerMeta := rt.NewTable() timerIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { @@ -164,18 +167,20 @@ func (th *timersModule) loader(rtm *rt.Runtime) *rt.Table { } timerMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(timerIndex, "__index", 2, false))) - l.SetRegistry(timerMetaKey, rt.TableValue(timerMeta)) + l.UnderlyingRuntime().SetRegistry(timerMetaKey, rt.TableValue(timerMeta)) - thExports := map[string]util.LuaExport{ + thExports := map[string]moonlight.Export{ + /* "create": {th.luaCreate, 3, false}, "get": {th.luaGet, 1, false}, + */ } - luaTh := rt.NewTable() - util.SetExports(rtm, luaTh, thExports) + luaTh := moonlight.NewTable() + l.SetExports(luaTh, thExports) - util.SetField(rtm, luaTh, "INTERVAL", rt.IntValue(0)) - util.SetField(rtm, luaTh, "TIMEOUT", rt.IntValue(1)) + luaTh.SetField("INTERVAL", rt.IntValue(0)) + luaTh.SetField("TIMEOUT", rt.IntValue(1)) return luaTh } @@ -200,6 +205,6 @@ func valueToTimer(val rt.Value) (*timer, bool) { } func timerUserData(j *timer) *rt.UserData { - timerMeta := l.Registry(timerMetaKey) + timerMeta := l.UnderlyingRuntime().Registry(timerMetaKey) return rt.NewUserData(j, timerMeta.AsTable()) } diff --git a/userdir.go b/userdir.go index a6c4852..3ccd9ee 100644 --- a/userdir.go +++ b/userdir.go @@ -1,7 +1,8 @@ package main import ( - "hilbish/util" + "hilbish/moonlight" + //"hilbish/util" rt "github.com/arnodel/golua/runtime" ) @@ -13,11 +14,11 @@ import ( // for configs and data. // #field config The user's config directory // #field data The user's directory for program data -func userDirLoader(rtm *rt.Runtime) *rt.Table { - mod := rt.NewTable() +func userDirLoader() *moonlight.Table { + mod := moonlight.NewTable() - util.SetField(rtm, mod, "config", rt.StringValue(confDir)) - util.SetField(rtm, mod, "data", rt.StringValue(userDataDir)) + mod.SetField("config", rt.StringValue(confDir)) + mod.SetField("data", rt.StringValue(userDataDir)) return mod } diff --git a/util/export.go b/util/export.go index ee0b4a6..9cb40de 100644 --- a/util/export.go +++ b/util/export.go @@ -1,12 +1,14 @@ package util import ( + "hilbish/moonlight" + rt "github.com/arnodel/golua/runtime" ) // LuaExport represents a Go function which can be exported to Lua. type LuaExport struct { - Function rt.GoFunctionFunc + Function moonlight.GoFunctionFunc ArgNum int Variadic bool } diff --git a/util/util.go b/util/util.go index 0fcd4b0..c50fae3 100644 --- a/util/util.go +++ b/util/util.go @@ -1,10 +1,7 @@ package util import ( - "bufio" - "io" "strings" - "os" "os/user" rt "github.com/arnodel/golua/runtime" @@ -12,81 +9,10 @@ import ( // 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. -func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value) { - // TODO: ^ rtm isnt needed, i should remove it +func SetField(module *rt.Table, field string, value rt.Value) { module.Set(rt.StringValue(field), value) } -// 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) { - realModule.Set(rt.StringValue(field), value) -} - -// DoString runs the code string in the Lua runtime. -func DoString(rtm *rt.Runtime, code string) (rt.Value, error) { - chunk, err := rtm.CompileAndLoadLuaChunk("", []byte(code), rt.TableValue(rtm.GlobalEnv())) - var ret rt.Value - if chunk != nil { - ret, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk)) - } - - return ret, 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...) - } - - clos, err := rtm.LoadFromSourceOrCode(path, buf, "bt", rt.TableValue(rtm.GlobalEnv()), false) - if clos != nil { - _, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(clos)) - } - - 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) { From e93ffdabb027079e986ac94cf841bce4852324ce Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 17:03:24 -0400 Subject: [PATCH 02/24] docs: add readme for midnight edition --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d6446c..9298aa0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -
+
🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨
@@ -9,6 +9,19 @@ Discord
+# Midnight Edition +> [!CAUTION] +> This is a **HEAVILY** WORK IN PROGRESS branch which makes a lot of internal changes. +Functionality **will** be missing if you use this branch, and you may see crashes too. +Tread lightly. + +Hilbish: Midinight Edition is a version of Hilbish meant to be compatible with +the original C implementation of Lua by using a Go library binding. The end goal +is to offer Midnight Edition as a separate, "not as supported" build for users +that *really* want to access a certain library made for C Lua. The standard edition, +which is all native Go, will always be more supported than Midnight Edition. + +# Back to the original README Hilbish is an extensible shell designed to be highly customizable. It is configured in Lua and provides a good range of features. It aims to be easy to use for anyone but powerful enough for From 43c467df509ba3cdf525d5a331e0d2abbc7b93be Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 17:03:56 -0400 Subject: [PATCH 03/24] chore: upload midnight edition logo --- .../hilbish-logo-and-text-midnight-edition.png | Bin 0 -> 101287 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/hilbish-logo-and-text-midnight-edition.png diff --git a/assets/hilbish-logo-and-text-midnight-edition.png b/assets/hilbish-logo-and-text-midnight-edition.png new file mode 100644 index 0000000000000000000000000000000000000000..19332c4ed29c9c795109f8f640c62c13b4efd20a GIT binary patch literal 101287 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 znu�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

0?C_j_RS{qG2r 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 S3NtpaPot)ysa&jR7W6*~N2+K5 literal 0 HcmV?d00001 From 4e589792f6e2eab4ebabc63e7618416674e87c30 Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 17:04:48 -0400 Subject: [PATCH 04/24] docs: add more details --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9298aa0..b921297 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,10 @@ Tread lightly. Hilbish: Midinight Edition is a version of Hilbish meant to be compatible with the original C implementation of Lua by using a Go library binding. The end goal is to offer Midnight Edition as a separate, "not as supported" build for users -that *really* want to access a certain library made for C Lua. The standard edition, -which is all native Go, will always be more supported than Midnight Edition. +that *really* want to access a certain library made for C Lua or want the +most performance with their Lua code. + +**The standard edition, which is all native Go, will always be more supported than Midnight Edition.** # Back to the original README Hilbish is an extensible shell designed to be highly customizable. From cdd2272e23d9047b917e71830e33cf7a9525e5fd Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 17:32:10 -0400 Subject: [PATCH 05/24] chore: make codacy happy --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b921297..4433fbc 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,12 @@ Discord
-# Midnight Edition +# Midnight Edition + > [!CAUTION] > This is a **HEAVILY** WORK IN PROGRESS branch which makes a lot of internal changes. -Functionality **will** be missing if you use this branch, and you may see crashes too. -Tread lightly. +Functionality **will** be missing if you use this branch, +and you may see crashes too. Tread lightly. Hilbish: Midinight Edition is a version of Hilbish meant to be compatible with the original C implementation of Lua by using a Go library binding. The end goal @@ -21,9 +22,11 @@ is to offer Midnight Edition as a separate, "not as supported" build for users that *really* want to access a certain library made for C Lua or want the most performance with their Lua code. -**The standard edition, which is all native Go, will always be more supported than Midnight Edition.** +**The standard edition, which is all native Go, +will always be more supported than Midnight Edition.** + +# Back to the original README -# Back to the original README Hilbish is an extensible shell designed to be highly customizable. It is configured in Lua and provides a good range of features. It aims to be easy to use for anyone but powerful enough for From 930a9747254795b7254ec9786bac095676049f08 Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 17:38:13 -0400 Subject: [PATCH 06/24] chore: make codacy happy (again) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4433fbc..5141790 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Discord
-# Midnight Edition +# Midnight Edition > [!CAUTION] > This is a **HEAVILY** WORK IN PROGRESS branch which makes a lot of internal changes. @@ -25,7 +25,7 @@ most performance with their Lua code. **The standard edition, which is all native Go, will always be more supported than Midnight Edition.** -# Back to the original README +# Back the original README Hilbish is an extensible shell designed to be highly customizable. It is configured in Lua and provides a good range of features. From af67bbd62592c7718ddf7474812ec156e90d023e Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 19 Jul 2024 17:43:32 -0400 Subject: [PATCH 07/24] docs: link pr on readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5141790..1fdb086 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Functionality **will** be missing if you use this branch, and you may see crashes too. Tread lightly. +Progress on Midnight Edition is tracked in this PR: [#314](https://github.com/Rosettea/Hilbish/pull/314) + Hilbish: Midinight Edition is a version of Hilbish meant to be compatible with the original C implementation of Lua by using a Go library binding. The end goal is to offer Midnight Edition as a separate, "not as supported" build for users From 56045ed236a23dbdcc91e44dceb17cd7c22536c2 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 10:50:04 -0400 Subject: [PATCH 08/24] feat: implement bait loading --- golibs/bait/bait.go | 31 ++++++++++++++----------------- golibs/commander/commander.go | 3 ++- lua.go | 9 ++++----- moonlight/function_golua.go | 1 + 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go index 1f85c76..09a7898 100644 --- a/golibs/bait/bait.go +++ b/golibs/bait/bait.go @@ -28,10 +28,10 @@ package bait import ( "errors" + "hilbish/moonlight" "hilbish/util" rt "github.com/arnodel/golua/runtime" - "github.com/arnodel/golua/lib/packagelib" ) type listenerType int @@ -48,26 +48,21 @@ type Listener struct{ typ listenerType once bool caller func(...interface{}) - luaCaller *rt.Closure + luaCaller *moonlight.Closure } type Bait struct{ - Loader packagelib.Loader recoverer Recoverer handlers map[string][]*Listener - rtm *rt.Runtime + rtm *moonlight.Runtime } // New creates a new Bait instance. -func New(rtm *rt.Runtime) *Bait { +func New(rtm *moonlight.Runtime) *Bait { b := &Bait{ handlers: make(map[string][]*Listener), rtm: rtm, } - b.Loader = packagelib.Loader{ - Load: b.loaderFunc, - Name: "bait", - } return b } @@ -88,16 +83,16 @@ func (b *Bait) Emit(event string, args ...interface{}) { if handle.typ == luaListener { funcVal := rt.FunctionValue(handle.luaCaller) - var luaArgs []rt.Value + var luaArgs []moonlight.Value for _, arg := range args { - var luarg rt.Value + var luarg moonlight.Value switch arg.(type) { case rt.Value: luarg = arg.(rt.Value) default: luarg = rt.AsValue(arg) } luaArgs = append(luaArgs, luarg) } - _, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...) + _, err := b.rtm.Call1(funcVal, luaArgs...) if err != nil { if event != "error" { b.Emit("error", event, handle.luaCaller, err.Error()) @@ -212,18 +207,20 @@ func (b *Bait) callRecoverer(event string, handler *Listener, err interface{}) { b.recoverer(event, handler, err) } -func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { - exports := map[string]util.LuaExport{ +func (b *Bait) Loader(rtm *moonlight.Runtime) moonlight.Value { + exports := map[string]moonlight.Export{ + /* "catch": util.LuaExport{b.bcatch, 2, false}, "catchOnce": util.LuaExport{b.bcatchOnce, 2, false}, "throw": util.LuaExport{b.bthrow, 1, true}, "release": util.LuaExport{b.brelease, 2, false}, "hooks": util.LuaExport{b.bhooks, 1, false}, + */ } - mod := rt.NewTable() - util.SetExports(rtm, mod, exports) + mod := moonlight.NewTable() + rtm.SetExports(mod, exports) - return rt.TableValue(mod), nil + return moonlight.TableValue(mod) } func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, args ...interface{}) { diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go index 840aaa1..1a9a90f 100644 --- a/golibs/commander/commander.go +++ b/golibs/commander/commander.go @@ -33,6 +33,7 @@ This sink is for writing errors, as the name would suggest. package commander import ( + "hilbish/moonlight" "hilbish/util" "hilbish/golibs/bait" @@ -46,7 +47,7 @@ type Commander struct{ Commands map[string]*rt.Closure } -func New(rtm *rt.Runtime) *Commander { +func New(rtm *moonlight.Runtime) *Commander { c := &Commander{ Events: bait.New(rtm), Commands: make(map[string]*rt.Closure), diff --git a/lua.go b/lua.go index 161fa38..3755f0e 100644 --- a/lua.go +++ b/lua.go @@ -5,7 +5,7 @@ import ( "os" //"hilbish/util" - //"hilbish/golibs/bait" + "hilbish/golibs/bait" //"hilbish/golibs/commander" //"hilbish/golibs/fs" //"hilbish/golibs/terminal" @@ -30,15 +30,14 @@ func luaInit() { cmds = commander.New(l) lib.LoadLibs(l, cmds.Loader) - +*/ hooks = bait.New(l) hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) { fmt.Println("Error in `error` hook handler:", err) hooks.Off(event, handler) }) - - lib.LoadLibs(l, hooks.Loader) - + l.LoadLibrary(hooks.Loader, "bait") +/* // Add Ctrl-C handler hooks.On("signal.sigint", func(...interface{}) { if !interactive { diff --git a/moonlight/function_golua.go b/moonlight/function_golua.go index ad019ff..c48bdd2 100644 --- a/moonlight/function_golua.go +++ b/moonlight/function_golua.go @@ -8,6 +8,7 @@ type GoFunctionFunc = rt.GoFunctionFunc type GoCont = rt.GoCont type Cont = rt.Cont +type Closure = rt.Closure func (mlr *Runtime) CheckNArgs(c *GoCont, num int) error { return c.CheckNArgs(num) From e335ef599475d6a292f44cb6af785e1356751a6d Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 10:53:49 -0400 Subject: [PATCH 09/24] feat: add userDir module --- api.go | 4 ++-- lua.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index c3081a2..1aca72f 100644 --- a/api.go +++ b/api.go @@ -81,8 +81,8 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { //util.SetField(rtm, mod, "vimMode", rt.NilValue) // hilbish.userDir table - //hshuser := userDirLoader(rtm) - //mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) + hshuser := userDirLoader() + hshMod.SetField("userDir", moonlight.TableValue(hshuser)) // hilbish.os table //hshos := hshosLoader(rtm) diff --git a/lua.go b/lua.go index 3755f0e..901dceb 100644 --- a/lua.go +++ b/lua.go @@ -53,6 +53,7 @@ func luaInit() { // Add more paths that Lua can require from _, err := l.DoString("package.path = package.path .. " + requirePaths) if err != nil { + fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.") } From 40a90c899b2e38649b49ed58c643b775b244f26e Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 11:00:34 -0400 Subject: [PATCH 10/24] docs: remove moonlight edition promo --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 62bf32a..1fdb086 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -> [!TIP] -> Check out [Hilbish: Midnight Edition](https://github.com/Rosettea/Hilbish/tree/midnight-edition) if you want to use C Lua, LuaJIT or anything related! -

🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨ From a5f695eb980cfae10b39c21958324978fea8a974 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 11:34:39 -0400 Subject: [PATCH 11/24] feat: implement parts of fs --- api.go | 5 ++-- exec.go | 4 +-- golibs/fs/fs.go | 56 +++++++++++++++++++------------------ lua.go | 6 ++-- module.go | 24 ++++++++-------- moonlight/cont_golua.go | 16 +++++++++++ moonlight/function_golua.go | 17 ++++++----- moonlight/runtime_golua.go | 9 ++++++ moonlight/table_golua.go | 4 +++ 9 files changed, 87 insertions(+), 54 deletions(-) create mode 100644 moonlight/cont_golua.go diff --git a/api.go b/api.go index 1aca72f..9428fe4 100644 --- a/api.go +++ b/api.go @@ -127,8 +127,9 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { //util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName)) //mod.Set(rt.StringValue("version"), rt.TableValue(versionModule)) - //pluginModule := moduleLoader(rtm) - //mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule)) + // very meta + moduleModule := moduleLoader(mlr) + hshMod.SetField("module", moonlight.TableValue(moduleModule)) return moonlight.TableValue(hshMod) } diff --git a/exec.go b/exec.go index 3b1d6ae..412b07a 100644 --- a/exec.go +++ b/exec.go @@ -352,7 +352,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc { sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud)) sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud)) - t := rt.NewThread(l) + //t := rt.NewThread(l) sig := make(chan os.Signal) exit := make(chan bool) @@ -368,7 +368,7 @@ func execHandle(bg bool) interp.ExecHandlerFunc { signal.Notify(sig, os.Interrupt) select { case <-sig: - t.KillContext() + //t.KillContext() return } diff --git a/golibs/fs/fs.go b/golibs/fs/fs.go index 13f972d..61ad799 100644 --- a/golibs/fs/fs.go +++ b/golibs/fs/fs.go @@ -14,50 +14,51 @@ import ( "os" "strings" + "hilbish/moonlight" "hilbish/util" rt "github.com/arnodel/golua/runtime" - "github.com/arnodel/golua/lib/packagelib" "github.com/arnodel/golua/lib/iolib" "mvdan.cc/sh/v3/interp" ) type fs struct{ runner *interp.Runner - Loader packagelib.Loader } func New(runner *interp.Runner) *fs { - f := &fs{ + return &fs{ runner: runner, } - f.Loader = packagelib.Loader{ - Load: f.loaderFunc, - Name: "fs", - } - - return f } -func (f *fs) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { - exports := map[string]util.LuaExport{ +func (f *fs) Loader(rtm *moonlight.Runtime) moonlight.Value { + exports := map[string]moonlight.Export{ + /* "cd": util.LuaExport{f.fcd, 1, false}, "mkdir": util.LuaExport{f.fmkdir, 2, false}, "stat": util.LuaExport{f.fstat, 1, false}, - "readdir": util.LuaExport{f.freaddir, 1, false}, + */ + "readdir": {f.freaddir, 1, false}, + /* "abs": util.LuaExport{f.fabs, 1, false}, "basename": util.LuaExport{f.fbasename, 1, false}, - "dir": util.LuaExport{f.fdir, 1, false}, + */ + "dir": {f.fdir, 1, false}, + /* "glob": util.LuaExport{f.fglob, 1, false}, "join": util.LuaExport{f.fjoin, 0, true}, "pipe": util.LuaExport{f.fpipe, 0, false}, + */ } - mod := rt.NewTable() - util.SetExports(rtm, mod, exports) - mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator))) - mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator))) - return rt.TableValue(mod), nil + mod := moonlight.NewTable() + rtm.SetExports(mod, exports) + + mod.SetField("pathSep", rt.StringValue(string(os.PathSeparator))) + mod.SetField("pathListSep", rt.StringValue(string(os.PathListSeparator))) + + return moonlight.TableValue(mod) } // abs(path) -> string @@ -124,16 +125,17 @@ func (f *fs) fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // `~/Documents/doc.txt` then this function will return `~/Documents`. // #param path string Path to get the directory for. // #returns string -func (f *fs) fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { +func (f *fs) fdir(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - path, err := c.StringArg(0) + path, err := mlr.StringArg(c, 0) if err != nil { return nil, err } - return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil + next := mlr.PushNext1(c, moonlight.StringValue(filepath.Dir(path))) + return next, nil } // glob(pattern) -> matches (table) @@ -262,16 +264,16 @@ func (f *fs) fpipe(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // Returns a list of all files and directories in the provided path. // #param dir string // #returns table -func (f *fs) freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { +func (f *fs) freaddir(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - dir, err := c.StringArg(0) + dir, err := mlr.StringArg(c, 0) if err != nil { return nil, err } dir = util.ExpandHome(dir) - names := rt.NewTable() + names := moonlight.NewTable() dirEntries, err := os.ReadDir(dir) if err != nil { @@ -281,7 +283,7 @@ func (f *fs) freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name())) } - return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil + return mlr.PushNext1(c, moonlight.TableValue(names)), nil } // stat(path) -> {} diff --git a/lua.go b/lua.go index 8cd8556..3f1bdc5 100644 --- a/lua.go +++ b/lua.go @@ -7,7 +7,7 @@ import ( //"hilbish/util" "hilbish/golibs/bait" //"hilbish/golibs/commander" - //"hilbish/golibs/fs" + "hilbish/golibs/fs" //"hilbish/golibs/terminal" "hilbish/moonlight" @@ -24,9 +24,9 @@ func luaInit() { l.DoString("hilbish = require 'hilbish'") // Add fs and terminal module module to Lua - /* f := fs.New(runner) - lib.LoadLibs(l, f.Loader) + l.LoadLibrary(f.Loader, "fs") + /* lib.LoadLibs(l, terminal.Loader) cmds = commander.New(l) diff --git a/module.go b/module.go index bf4e32a..e0a6efd 100644 --- a/module.go +++ b/module.go @@ -3,9 +3,7 @@ package main import ( "plugin" - "hilbish/util" - - rt "github.com/arnodel/golua/runtime" + "hilbish/moonlight" ) // #interface module @@ -46,13 +44,13 @@ func Loader(rtm *rt.Runtime) rt.Value { This can be compiled with `go build -buildmode=plugin plugin.go`. If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!" */ -func moduleLoader(rtm *rt.Runtime) *rt.Table { - exports := map[string]util.LuaExport{ +func moduleLoader(mlr *moonlight.Runtime) *moonlight.Table { + exports := map[string]moonlight.Export{ "load": {moduleLoad, 2, false}, } - mod := rt.NewTable() - util.SetExports(rtm, mod, exports) + mod := moonlight.NewTable() + mlr.SetExports(mod, exports) return mod } @@ -62,12 +60,12 @@ func moduleLoader(rtm *rt.Runtime) *rt.Table { // Loads a module at the designated `path`. // It will throw if any error occurs. // #param path string -func moduleLoad(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.CheckNArgs(1); err != nil { +func moduleLoad(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - path, err := c.StringArg(0) + path, err := mlr.StringArg(c, 0) if err != nil { return nil, err } @@ -82,12 +80,12 @@ func moduleLoad(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, err } - loader, ok := value.(func(*rt.Runtime) rt.Value) + loader, ok := value.(func(*moonlight.Runtime) moonlight.Value) if !ok { return nil, nil } - val := loader(t.Runtime) + val := loader(mlr) - return c.PushingNext1(t.Runtime, val), nil + return mlr.PushNext1(c, val), nil } diff --git a/moonlight/cont_golua.go b/moonlight/cont_golua.go new file mode 100644 index 0000000..d34993e --- /dev/null +++ b/moonlight/cont_golua.go @@ -0,0 +1,16 @@ +package moonlight + +import ( + rt "github.com/arnodel/golua/runtime" +) + +type GoCont struct{ + cont *rt.GoCont + thread *rt.Thread +} +type Cont = rt.Cont +type Closure = rt.Closure + +func (gc *GoCont) Next() Cont { + return gc.cont.Next() +} diff --git a/moonlight/function_golua.go b/moonlight/function_golua.go index c48bdd2..5e882ae 100644 --- a/moonlight/function_golua.go +++ b/moonlight/function_golua.go @@ -6,21 +6,24 @@ import ( type GoFunctionFunc = rt.GoFunctionFunc -type GoCont = rt.GoCont -type Cont = rt.Cont -type Closure = rt.Closure - func (mlr *Runtime) CheckNArgs(c *GoCont, num int) error { - return c.CheckNArgs(num) + return c.cont.CheckNArgs(num) +} + +func (mlr *Runtime) Check1Arg(c *GoCont) error { + return c.cont.CheckNArgs(1) } func (mlr *Runtime) StringArg(c *GoCont, num int) (string, error) { - return c.StringArg(num) + return c.cont.StringArg(num) } func (mlr *Runtime) GoFunction(fun GoToLuaFunc) rt.GoFunctionFunc { return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - gocont := GoCont(*c) + gocont := GoCont{ + cont: c, + thread: t, + } return fun(mlr, &gocont) } } diff --git a/moonlight/runtime_golua.go b/moonlight/runtime_golua.go index 6072ea4..095931f 100644 --- a/moonlight/runtime_golua.go +++ b/moonlight/runtime_golua.go @@ -33,3 +33,12 @@ func specificRuntimeToGeneric(rtm *rt.Runtime) *Runtime { func (mlr *Runtime) UnderlyingRuntime() *rt.Runtime { return mlr.rt } + +// Push will push a Lua value onto the stack. +func (mlr *Runtime) Push(c *GoCont, v Value) { + c.cont.Push(c.thread.Runtime, v) +} + +func (mlr *Runtime) PushNext1(c *GoCont, v Value) Cont { + return c.cont.PushingNext1(c.thread.Runtime, v) +} diff --git a/moonlight/table_golua.go b/moonlight/table_golua.go index 3c64eb9..4f2ff36 100644 --- a/moonlight/table_golua.go +++ b/moonlight/table_golua.go @@ -22,6 +22,10 @@ func (t *Table) SetField(key string, value Value) { t.lt.Set(rt.StringValue(key), value) } +func (t *Table) Set(key Value, value Value) { + t.lt.Set(key, value) +} + func (mlr *Runtime) GlobalTable() *Table { return &Table{ lt: mlr.rt.GlobalEnv(), From 57a65cb039e31d956906105f1a14d5261d745229 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 11:46:07 -0400 Subject: [PATCH 12/24] feat: add moonlight to commander --- golibs/bait/bait.go | 10 ++++++-- golibs/commander/commander.go | 44 +++++++++++++++-------------------- lua.go | 8 +++---- moonlight/function_golua.go | 4 ++++ util/util.go | 10 ++++---- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go index 09a7898..9359bd9 100644 --- a/golibs/bait/bait.go +++ b/golibs/bait/bait.go @@ -26,10 +26,10 @@ this function will set the user prompt. package bait import ( - "errors" + //"errors" "hilbish/moonlight" - "hilbish/util" + //"hilbish/util" rt "github.com/arnodel/golua/runtime" ) @@ -255,6 +255,7 @@ bait.catch('hilbish.exit', function() end) #example */ +/* func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { name, catcher, err := util.HandleStrCallback(t, c) if err != nil { @@ -312,6 +313,7 @@ func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.TableValue(luaHandlers)), nil } +*/ // release(name, catcher) // Removes the `catcher` for the event with `name`. @@ -330,6 +332,7 @@ bait.release('event', hookCallback) -- and now hookCallback will no longer be ran for the event. #example */ +/* func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { name, catcher, err := util.HandleStrCallback(t, c) if err != nil { @@ -340,6 +343,7 @@ func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +*/ // throw(name, ...args) // #param name string The name of the hook. @@ -355,6 +359,7 @@ bait.catch('gretting', function(greetTo) end) #example */ +/* func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { return nil, err @@ -371,3 +376,4 @@ func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +*/ diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go index 1a9a90f..53d55f3 100644 --- a/golibs/commander/commander.go +++ b/golibs/commander/commander.go @@ -38,12 +38,10 @@ import ( "hilbish/golibs/bait" rt "github.com/arnodel/golua/runtime" - "github.com/arnodel/golua/lib/packagelib" ) type Commander struct{ Events *bait.Bait - Loader packagelib.Loader Commands map[string]*rt.Closure } @@ -52,24 +50,20 @@ func New(rtm *moonlight.Runtime) *Commander { Events: bait.New(rtm), Commands: make(map[string]*rt.Closure), } - c.Loader = packagelib.Loader{ - Load: c.loaderFunc, - Name: "commander", - } return c } -func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { - exports := map[string]util.LuaExport{ - "register": util.LuaExport{c.cregister, 2, false}, - "deregister": util.LuaExport{c.cderegister, 1, false}, - "registry": util.LuaExport{c.cregistry, 0, false}, +func (c *Commander) Loader(rtm *moonlight.Runtime) moonlight.Value { + exports := map[string]moonlight.Export{ + "register": {c.cregister, 2, false}, + "deregister": {c.cderegister, 1, false}, + "registry": {c.cregistry, 0, false}, } - mod := rt.NewTable() - util.SetExports(rtm, mod, exports) + mod := moonlight.NewTable() + rtm.SetExports(mod, exports) - return rt.TableValue(mod), nil + return moonlight.TableValue(mod) } // register(name, cb) @@ -89,8 +83,8 @@ commander.register('hello', function(args, sinks) end) #example */ -func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { - cmdName, cmd, err := util.HandleStrCallback(t, ct) +func (c *Commander) cregister(mlr *moonlight.Runtime, ct *moonlight.GoCont) (moonlight.Cont, error) { + cmdName, cmd, err := util.HandleStrCallback(mlr, ct) if err != nil { return nil, err } @@ -103,11 +97,11 @@ func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { // deregister(name) // Removes the named command. Note that this will only remove Commander-registered commands. // #param name string Name of the command to remove. -func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { - if err := ct.Check1Arg(); err != nil { +func (c *Commander) cderegister(mlr *moonlight.Runtime, ct *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(ct); err != nil { return nil, err } - cmdName, err := ct.StringArg(0) + cmdName, err := mlr.StringArg(ct, 0) if err != nil { return nil, err } @@ -121,14 +115,14 @@ func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { // Returns all registered commanders. Returns a list of tables with the following keys: // - `exec`: The function used to run the commander. Commanders require args and sinks to be passed. // #returns table -func (c *Commander) cregistry(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { - registryLua := rt.NewTable() +func (c *Commander) cregistry(mlr *moonlight.Runtime, ct *moonlight.GoCont) (moonlight.Cont, error) { + registryLua := moonlight.NewTable() for cmdName, cmd := range c.Commands { - cmdTbl := rt.NewTable() - cmdTbl.Set(rt.StringValue("exec"), rt.FunctionValue(cmd)) + cmdTbl := moonlight.NewTable() + cmdTbl.SetField("exec", rt.FunctionValue(cmd)) - registryLua.Set(rt.StringValue(cmdName), rt.TableValue(cmdTbl)) + registryLua.SetField(cmdName, moonlight.TableValue(cmdTbl)) } - return ct.PushingNext1(t.Runtime, rt.TableValue(registryLua)), nil + return mlr.PushNext1(ct, moonlight.TableValue(registryLua)), nil } diff --git a/lua.go b/lua.go index 3f1bdc5..69dc5b6 100644 --- a/lua.go +++ b/lua.go @@ -6,7 +6,7 @@ import ( //"hilbish/util" "hilbish/golibs/bait" - //"hilbish/golibs/commander" + "hilbish/golibs/commander" "hilbish/golibs/fs" //"hilbish/golibs/terminal" @@ -28,10 +28,10 @@ func luaInit() { l.LoadLibrary(f.Loader, "fs") /* lib.LoadLibs(l, terminal.Loader) - - cmds = commander.New(l) - lib.LoadLibs(l, cmds.Loader) */ + cmds = commander.New(l) + l.LoadLibrary(cmds.Loader, "commander") + hooks = bait.New(l) hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) { fmt.Println("Error in `error` hook handler:", err) diff --git a/moonlight/function_golua.go b/moonlight/function_golua.go index 5e882ae..ad30337 100644 --- a/moonlight/function_golua.go +++ b/moonlight/function_golua.go @@ -18,6 +18,10 @@ func (mlr *Runtime) StringArg(c *GoCont, num int) (string, error) { return c.cont.StringArg(num) } +func (mlr *Runtime) ClosureArg(c *GoCont, num int) (*Closure, error) { + return c.cont.ClosureArg(num) +} + func (mlr *Runtime) GoFunction(fun GoToLuaFunc) rt.GoFunctionFunc { return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { gocont := GoCont{ diff --git a/util/util.go b/util/util.go index c50fae3..46a6a2d 100644 --- a/util/util.go +++ b/util/util.go @@ -4,6 +4,8 @@ import ( "strings" "os/user" + "hilbish/moonlight" + rt "github.com/arnodel/golua/runtime" ) @@ -15,15 +17,15 @@ func SetField(module *rt.Table, field string, value rt.Value) { // 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 { +func HandleStrCallback(mlr *moonlight.Runtime, c *moonlight.GoCont) (string, *moonlight.Closure, error) { + if err := mlr.CheckNArgs(c, 2); err != nil { return "", nil, err } - name, err := c.StringArg(0) + name, err := mlr.StringArg(c, 0) if err != nil { return "", nil, err } - cb, err := c.ClosureArg(1) + cb, err := mlr.ClosureArg(c, 1) if err != nil { return "", nil, err } From 80e6dedf9ecb080182079ad7f92466abe76c4202 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 11:47:59 -0400 Subject: [PATCH 13/24] feat: rewrite hilbish.cwd for moonlight --- api.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index 9428fe4..566fb20 100644 --- a/api.go +++ b/api.go @@ -42,7 +42,9 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { /* "appendPath": {hlappendPath, 1, false}, "complete": {hlcomplete, 2, false}, + */ "cwd": {hlcwd, 0, false}, + /* "exec": {hlexec, 1, false}, "runnerMode": {hlrunnerMode, 1, false}, "goro": {hlgoro, 1, true}, @@ -289,10 +291,10 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // cwd() -> string // Returns the current directory of the shell. // #returns string -func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +func hlcwd(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { cwd, _ := os.Getwd() - return c.PushingNext1(t.Runtime, rt.StringValue(cwd)), nil + return mlr.PushNext1(c, moonlight.StringValue(cwd)), nil } From f37f5b7ddcb7c86acf7e9c020e137ebc973e6f38 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 11:59:14 -0400 Subject: [PATCH 14/24] refactor: rewrite terminal library with moonlight --- golibs/terminal/terminal.go | 37 ++++++++++++++++--------------------- lua.go | 8 ++++---- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/golibs/terminal/terminal.go b/golibs/terminal/terminal.go index 954a4dd..e1af46d 100644 --- a/golibs/terminal/terminal.go +++ b/golibs/terminal/terminal.go @@ -5,52 +5,47 @@ package terminal import ( "os" - "hilbish/util" + "hilbish/moonlight" rt "github.com/arnodel/golua/runtime" - "github.com/arnodel/golua/lib/packagelib" "golang.org/x/term" ) var termState *term.State -var Loader = packagelib.Loader{ - Load: loaderFunc, - Name: "terminal", -} -func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { - exports := map[string]util.LuaExport{ - "setRaw": util.LuaExport{termsetRaw, 0, false}, - "restoreState": util.LuaExport{termrestoreState, 0, false}, - "size": util.LuaExport{termsize, 0, false}, - "saveState": util.LuaExport{termsaveState, 0, false}, +func Loader(rtm *moonlight.Runtime) moonlight.Value { + exports := map[string]moonlight.Export{ + "setRaw": {termsetRaw, 0, false}, + "restoreState": {termrestoreState, 0, false}, + "size": {termsize, 0, false}, + "saveState": {termsaveState, 0, false}, } - mod := rt.NewTable() - util.SetExports(rtm, mod, exports) + mod := moonlight.NewTable() + rtm.SetExports(mod, exports) - return rt.TableValue(mod), nil + return moonlight.TableValue(mod) } // size() // Gets the dimensions of the terminal. Returns a table with `width` and `height` // NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal. -func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +func termsize(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { w, h, err := term.GetSize(int(os.Stdin.Fd())) if err != nil { return nil, err } - dimensions := rt.NewTable() + dimensions := moonlight.NewTable() dimensions.Set(rt.StringValue("width"), rt.IntValue(int64(w))) dimensions.Set(rt.StringValue("height"), rt.IntValue(int64(h))) - return c.PushingNext1(t.Runtime, rt.TableValue(dimensions)), nil + return mlr.PushNext1(c, moonlight.TableValue(dimensions)), nil } // saveState() // Saves the current state of the terminal. -func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +func termsaveState(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { state, err := term.GetState(int(os.Stdin.Fd())) if err != nil { return nil, err @@ -62,7 +57,7 @@ func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // restoreState() // Restores the last saved state of the terminal -func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +func termrestoreState(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { err := term.Restore(int(os.Stdin.Fd()), termState) if err != nil { return nil, err @@ -73,7 +68,7 @@ func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // setRaw() // Puts the terminal into raw mode. -func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { +func termsetRaw(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { _, err := term.MakeRaw(int(os.Stdin.Fd())) if err != nil { return nil, err diff --git a/lua.go b/lua.go index 69dc5b6..9e05af1 100644 --- a/lua.go +++ b/lua.go @@ -8,7 +8,7 @@ import ( "hilbish/golibs/bait" "hilbish/golibs/commander" "hilbish/golibs/fs" - //"hilbish/golibs/terminal" + "hilbish/golibs/terminal" "hilbish/moonlight" ) @@ -26,9 +26,9 @@ func luaInit() { // Add fs and terminal module module to Lua f := fs.New(runner) l.LoadLibrary(f.Loader, "fs") - /* - lib.LoadLibs(l, terminal.Loader) -*/ + + l.LoadLibrary(terminal.Loader, "terminal") + cmds = commander.New(l) l.LoadLibrary(cmds.Loader, "commander") From 4d07f166b6caeefedddca71369af9a130815d451 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 12:12:06 -0400 Subject: [PATCH 15/24] refactor: rewrite hilbish.prompt for moonlight --- api.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/api.go b/api.go index 566fb20..b0fc81b 100644 --- a/api.go +++ b/api.go @@ -52,7 +52,9 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { "hinter": {hlhinter, 1, false}, "multiprompt": {hlmultiprompt, 1, false}, "prependPath": {hlprependPath, 1, false}, + */ "prompt": {hlprompt, 1, true}, + /* "inputMode": {hlinputMode, 1, false}, "interval": {hlinterval, 2, false}, "read": {hlread, 1, false}, @@ -347,17 +349,18 @@ hilbish.prompt '%u@%h :%d $' -- prompt: user@hostname: ~/directory $ #example */ -func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - err := c.Check1Arg() +func hlprompt(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + err := mlr.Check1Arg(c) if err != nil { return nil, err } - p, err := c.StringArg(0) + p, err := mlr.StringArg(c, 0) if err != nil { return nil, err } typ := "left" // optional 2nd arg + /* if len(c.Etc()) != 0 { ltyp := c.Etc()[0] var ok bool @@ -366,6 +369,7 @@ func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, errors.New("bad argument to run (expected string, got " + ltyp.TypeName() + ")") } } + */ switch typ { case "left": From 0c904321f47e8d116a7049f631e67cba28d56a03 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 14:15:26 -0400 Subject: [PATCH 16/24] fix: initialize some modules --- api.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api.go b/api.go index b0fc81b..6c8d501 100644 --- a/api.go +++ b/api.go @@ -93,7 +93,7 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { //mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) // hilbish.aliases table - //aliases = newAliases() + aliases = newAliases() //aliasesModule := aliases.Loader(rtm) //mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) @@ -112,12 +112,12 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { //mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) // hilbish.jobs table - //jobs = newJobHandler() + jobs = newJobHandler() //jobModule := jobs.loader(rtm) //mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) // hilbish.timers table - //timers = newTimersModule() + timers = newTimersModule() //timersModule := timers.loader(rtm) //mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule)) From 69fcd8e34857a018f849fd242b30fd91aa3d4ba5 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 14:49:43 -0400 Subject: [PATCH 17/24] refactor: rewrite runner module for moonlight, add hilbish.runnerMode function --- api.go | 20 ++++++++------ moonlight/function_golua.go | 4 +++ moonlight/value_golua.go | 9 ++++++ runnermode.go | 55 ++++++++++++++++++------------------- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/api.go b/api.go index 6c8d501..52c4209 100644 --- a/api.go +++ b/api.go @@ -46,7 +46,9 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { "cwd": {hlcwd, 0, false}, /* "exec": {hlexec, 1, false}, + */ "runnerMode": {hlrunnerMode, 1, false}, + /* "goro": {hlgoro, 1, true}, "highlighter": {hlhighlighter, 1, false}, "hinter": {hlhinter, 1, false}, @@ -108,8 +110,8 @@ func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { //mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp)) // hilbish.runner table - //runnerModule := runnerModeLoader(rtm) - //mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) + runnerModule := runnerModeLoader(mlr) + hshMod.SetField("runner", moonlight.TableValue(runnerModule)) // hilbish.jobs table jobs = newJobHandler() @@ -749,6 +751,7 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +*/ // runnerMode(mode) // Sets the execution/runner mode for interactive Hilbish. @@ -759,25 +762,24 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // will call it to execute user input instead. // Read [about runner mode](../features/runner-mode) for more information. // #param mode string|function -func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { +func hlrunnerMode(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - mode := c.Arg(0) + mode := mlr.Arg(c, 0) - switch mode.Type() { - case rt.StringType: + switch moonlight.Type(mode) { + case moonlight.StringType: switch mode.AsString() { case "hybrid", "hybridRev", "lua", "sh": runnerMode = mode default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.AsString()) } - case rt.FunctionType: runnerMode = mode + case moonlight.FunctionType: runnerMode = mode default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.TypeName()) } return c.Next(), nil } -*/ // hinter(line, pos) // The command line hint handler. It gets called on every key insert to diff --git a/moonlight/function_golua.go b/moonlight/function_golua.go index ad30337..5597968 100644 --- a/moonlight/function_golua.go +++ b/moonlight/function_golua.go @@ -22,6 +22,10 @@ func (mlr *Runtime) ClosureArg(c *GoCont, num int) (*Closure, error) { return c.cont.ClosureArg(num) } +func (mlr *Runtime) Arg(c *GoCont, num int) Value { + return c.cont.Arg(num) +} + func (mlr *Runtime) GoFunction(fun GoToLuaFunc) rt.GoFunctionFunc { return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { gocont := GoCont{ diff --git a/moonlight/value_golua.go b/moonlight/value_golua.go index 2300da4..1233563 100644 --- a/moonlight/value_golua.go +++ b/moonlight/value_golua.go @@ -5,6 +5,11 @@ import ( ) type Value = rt.Value +type ValueType = rt.ValueType +const ( + StringType = rt.StringType + FunctionType = rt.FunctionType +) func StringValue(str string) Value { return rt.StringValue(str) @@ -21,3 +26,7 @@ func BoolValue(b bool) Value { func TableValue(t *Table) Value { return rt.TableValue(t.lt) } + +func Type(v Value) ValueType { + return ValueType(v.Type()) +} diff --git a/runnermode.go b/runnermode.go index e66e5fa..3394877 100644 --- a/runnermode.go +++ b/runnermode.go @@ -1,7 +1,7 @@ package main import ( - "hilbish/util" + "hilbish/moonlight" rt "github.com/arnodel/golua/runtime" ) @@ -49,17 +49,15 @@ hilbish.runnerMode(function(input) end) ``` */ -func runnerModeLoader(rtm *rt.Runtime) *rt.Table { - exports := map[string]util.LuaExport{ - /* +func runnerModeLoader(rtm *moonlight.Runtime) *moonlight.Table { + exports := map[string]moonlight.Export{ "sh": {shRunner, 1, false}, "lua": {luaRunner, 1, false}, "setMode": {hlrunnerMode, 1, false}, - */ } - mod := rt.NewTable() - util.SetExports(rtm, mod, exports) + mod := moonlight.NewTable() + rtm.SetExports(mod, exports) return mod } @@ -78,27 +76,27 @@ func _runnerMode() {} // Runs a command in Hilbish's shell script interpreter. // This is the equivalent of using `source`. // #param cmd string -func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { +func shRunner(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - cmd, err := c.StringArg(0) + cmd, err := mlr.StringArg(c, 0) if err != nil { return nil, err } _, exitCode, cont, err := execSh(aliases.Resolve(cmd)) - var luaErr rt.Value = rt.NilValue + var luaErr moonlight.Value = rt.NilValue if err != nil { - luaErr = rt.StringValue(err.Error()) + luaErr = moonlight.StringValue(err.Error()) } - runnerRet := rt.NewTable() - runnerRet.Set(rt.StringValue("input"), rt.StringValue(cmd)) - runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode))) - runnerRet.Set(rt.StringValue("continue"), rt.BoolValue(cont)) - runnerRet.Set(rt.StringValue("err"), luaErr) + runnerRet := moonlight.NewTable() + runnerRet.SetField("input", moonlight.StringValue(cmd)) + runnerRet.SetField("exitCode", moonlight.IntValue(int(exitCode))) + runnerRet.SetField("continue", moonlight.BoolValue(cont)) + runnerRet.SetField("err", luaErr) - return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil + return mlr.PushNext1(c, moonlight.TableValue(runnerRet)), nil } // #interface runner @@ -106,24 +104,25 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // Evaluates `cmd` as Lua input. This is the same as using `dofile` // or `load`, but is appropriated for the runner interface. // #param cmd string -func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { +func luaRunner(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - cmd, err := c.StringArg(0) + cmd, err := mlr.StringArg(c, 0) if err != nil { return nil, err } input, exitCode, err := handleLua(cmd) - var luaErr rt.Value = rt.NilValue + var luaErr moonlight.Value = rt.NilValue if err != nil { - luaErr = rt.StringValue(err.Error()) + luaErr = moonlight.StringValue(err.Error()) } - runnerRet := rt.NewTable() - runnerRet.Set(rt.StringValue("input"), rt.StringValue(input)) - runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode))) - runnerRet.Set(rt.StringValue("err"), luaErr) + runnerRet := moonlight.NewTable() + runnerRet.SetField("input", moonlight.StringValue(input)) + runnerRet.SetField("exitCode", moonlight.IntValue(int(exitCode))) + runnerRet.SetField("err", luaErr) - return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil + + return mlr.PushNext1(c, moonlight.TableValue(runnerRet)), nil } From 4fdc99db886f123e0878b743a434fcb1ccc9fb61 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 15:11:45 -0400 Subject: [PATCH 18/24] refactor: rewrite appendPath to use moonlight --- api.go | 20 +++++++++++--------- moonlight/table_golua.go | 19 +++++++++++++++++++ moonlight/value_golua.go | 9 +++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/api.go b/api.go index 52c4209..73f8b3d 100644 --- a/api.go +++ b/api.go @@ -39,8 +39,8 @@ var hshMod *moonlight.Table func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { var exports = map[string]moonlight.Export{ "alias": {hlalias, 2, false}, - /* "appendPath": {hlappendPath, 1, false}, + /* "complete": {hlcomplete, 2, false}, */ "cwd": {hlcwd, 0, false}, @@ -468,20 +468,21 @@ hilbish.appendPath { '~/.local/bin' } #example -func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - if err := c.Check1Arg(); err != nil { +*/ +func hlappendPath(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + if err := mlr.Check1Arg(c); err != nil { return nil, err } - arg := c.Arg(0) + arg := mlr.Arg(c, 0) // check if dir is a table or a string - if arg.Type() == rt.TableType { - util.ForEach(arg.AsTable(), func(k rt.Value, v rt.Value) { - if v.Type() == rt.StringType { - appendPath(v.AsString()) + if moonlight.Type(arg) == moonlight.TableType { + moonlight.ForEach(moonlight.ToTable(arg), func(_ moonlight.Value, v moonlight.Value) { + if moonlight.Type(v) == moonlight.StringType { + appendPath(moonlight.ToString(v)) } }) - } else if arg.Type() == rt.StringType { + } else if moonlight.Type(arg) == moonlight.StringType { appendPath(arg.AsString()) } else { return nil, errors.New("bad argument to appendPath (expected string or table, got " + arg.TypeName() + ")") @@ -500,6 +501,7 @@ func appendPath(dir string) { } } +/* // exec(cmd) // Replaces the currently running Hilbish instance with the supplied command. // This can be used to do an in-place restart. diff --git a/moonlight/table_golua.go b/moonlight/table_golua.go index 4f2ff36..8c0238f 100644 --- a/moonlight/table_golua.go +++ b/moonlight/table_golua.go @@ -26,8 +26,27 @@ func (t *Table) Set(key Value, value Value) { t.lt.Set(key, value) } +func ForEach(tbl *Table, cb func(key Value, val Value)) { + nextVal := rt.NilValue + for { + key, val, _ := tbl.lt.Next(nextVal) + if key == rt.NilValue { + break + } + nextVal = key + + cb(Value(key), Value(val)) + } +} + func (mlr *Runtime) GlobalTable() *Table { return &Table{ lt: mlr.rt.GlobalEnv(), } } + +func convertToMoonlightTable(t *rt.Table) *Table { + return &Table{ + lt: t, + } +} diff --git a/moonlight/value_golua.go b/moonlight/value_golua.go index 1233563..2c74394 100644 --- a/moonlight/value_golua.go +++ b/moonlight/value_golua.go @@ -9,6 +9,7 @@ type ValueType = rt.ValueType const ( StringType = rt.StringType FunctionType = rt.FunctionType + TableType = rt.TableType ) func StringValue(str string) Value { @@ -30,3 +31,11 @@ func TableValue(t *Table) Value { func Type(v Value) ValueType { return ValueType(v.Type()) } + +func ToString(v Value) string { + return v.AsString() +} + +func ToTable(v Value) *Table { + return convertToMoonlightTable(v.AsTable()) +} From 17a3e2c0c4734a4c1ccffb72d6a4c343f54df376 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sat, 20 Jul 2024 15:18:05 -0400 Subject: [PATCH 19/24] refactor(bait): catch function --- golibs/bait/bait.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go index 9359bd9..cbe58cd 100644 --- a/golibs/bait/bait.go +++ b/golibs/bait/bait.go @@ -29,7 +29,7 @@ import ( //"errors" "hilbish/moonlight" - //"hilbish/util" + "hilbish/util" rt "github.com/arnodel/golua/runtime" ) @@ -87,7 +87,7 @@ func (b *Bait) Emit(event string, args ...interface{}) { for _, arg := range args { var luarg moonlight.Value switch arg.(type) { - case rt.Value: luarg = arg.(rt.Value) + case moonlight.Value: luarg = arg.(moonlight.Value) default: luarg = rt.AsValue(arg) } luaArgs = append(luaArgs, luarg) @@ -124,8 +124,8 @@ func (b *Bait) On(event string, handler func(...interface{})) *Listener { } // OnLua adds a Lua function handler for an event. -func (b *Bait) OnLua(event string, handler *rt.Closure) *Listener { - listener :=&Listener{ +func (b *Bait) OnLua(event string, handler *moonlight.Closure) *Listener { + listener := &Listener{ typ: luaListener, luaCaller: handler, } @@ -209,8 +209,8 @@ func (b *Bait) callRecoverer(event string, handler *Listener, err interface{}) { func (b *Bait) Loader(rtm *moonlight.Runtime) moonlight.Value { exports := map[string]moonlight.Export{ + "catch": {b.bcatch, 2, false}, /* - "catch": util.LuaExport{b.bcatch, 2, false}, "catchOnce": util.LuaExport{b.bcatchOnce, 2, false}, "throw": util.LuaExport{b.bthrow, 1, true}, "release": util.LuaExport{b.brelease, 2, false}, @@ -255,9 +255,8 @@ bait.catch('hilbish.exit', function() end) #example */ -/* -func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { - name, catcher, err := util.HandleStrCallback(t, c) +func (b *Bait) bcatch(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, error) { + name, catcher, err := util.HandleStrCallback(mlr, c) if err != nil { return nil, err } @@ -267,6 +266,7 @@ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +/* // catchOnce(name, cb) // Catches an event, but only once. This will remove the hook immediately after it runs for the first time. // #param name string The name of the event From 1f9fd80fbb33f68e24bfe52b42e7ddbfce042b49 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sun, 21 Jul 2024 11:37:08 -0400 Subject: [PATCH 20/24] feat: begin initial port to c lua --- .github/workflows/build.yml | 3 +- README.md | 7 +- Taskfile.yaml | 8 +++ aliases.go | 4 +- api.go | 4 +- editor.go | 2 + exec.go | 52 ++++----------- go.mod | 1 + go.sum | 2 + golibs/bait/bait.go | 10 +-- golibs/commander/commander.go | 10 +-- golibs/fs/fs.go | 6 +- golibs/terminal/terminal.go | 5 +- history.go | 18 ++--- job.go | 4 +- lua_exec.go | 36 ++++++++++ lua_exec_midnight.go | 10 +++ main.go | 6 +- moonlight/closure_clua.go | 28 ++++++++ moonlight/cont_clua.go | 12 ++++ moonlight/cont_golua.go | 2 + moonlight/export.go | 7 ++ moonlight/export_clua.go | 8 +++ moonlight/export_golua.go | 7 +- moonlight/function_clua.go | 61 +++++++++++++++++ moonlight/function_golua.go | 7 +- moonlight/loader_clua.go | 21 ++++++ moonlight/loader_golua.go | 1 + moonlight/runtime_clua.go | 49 ++++++++++++++ moonlight/runtime_golua.go | 5 ++ moonlight/table_clua.go | 43 ++++++++++++ moonlight/table_golua.go | 7 ++ moonlight/util_clua.go | 12 ++++ moonlight/util_golua.go | 3 +- moonlight/value_clua.go | 121 ++++++++++++++++++++++++++++++++++ moonlight/value_golua.go | 16 +++-- os.go | 7 +- rl.go | 99 +++------------------------- rl_midnight.go | 10 +++ rl_notmidnight.go | 97 +++++++++++++++++++++++++++ runnermode.go | 10 ++- sink.go | 6 +- timer.go | 10 ++- timerhandler.go | 10 ++- userdir.go | 7 +- util/export.go | 2 + 46 files changed, 651 insertions(+), 205 deletions(-) create mode 100644 lua_exec.go create mode 100644 lua_exec_midnight.go create mode 100644 moonlight/closure_clua.go create mode 100644 moonlight/cont_clua.go create mode 100644 moonlight/export.go create mode 100644 moonlight/export_clua.go create mode 100644 moonlight/function_clua.go create mode 100644 moonlight/loader_clua.go create mode 100644 moonlight/runtime_clua.go create mode 100644 moonlight/table_clua.go create mode 100644 moonlight/util_clua.go create mode 100644 moonlight/value_clua.go create mode 100644 rl_midnight.go create mode 100644 rl_notmidnight.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1fe1b1..efb2fa1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: + midnight: [false, true] goos: [linux, windows, darwin] goarch: ["386", amd64, arm64] exclude: @@ -33,7 +34,7 @@ jobs: - name: Download Task run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d' - name: Build - run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task + run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task ${{ matrix.midnight == 'true' && 'midnight' || ''}} - uses: actions/upload-artifact@v4 if: matrix.goos == 'windows' with: diff --git a/README.md b/README.md index 1fdb086..c34ea12 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ Functionality **will** be missing if you use this branch, and you may see crashes too. Tread lightly. -Progress on Midnight Edition is tracked in this PR: [#314](https://github.com/Rosettea/Hilbish/pull/314) +Build instructions and progress on Midnight Edition is tracked in this PR: +[#314](https://github.com/Rosettea/Hilbish/pull/314) Hilbish: Midinight Edition is a version of Hilbish meant to be compatible with the original C implementation of Lua by using a Go library binding. The end goal @@ -70,13 +71,13 @@ go get -d ./... To build, run: ``` -task +go-task ``` Or, if you want a stable branch, run these commands: ``` git checkout $(git describe --tags `git rev-list --tags --max-count=1`) -task build +go-task build ``` After you did all that, run `sudo task install` to install Hilbish globally. diff --git a/Taskfile.yaml b/Taskfile.yaml index 264e7d5..04f7667 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -10,6 +10,8 @@ vars: LIBDIR: '{{default .libdir__ .LIBDIR}}' goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"' GOFLAGS: '{{default .goflags__ .GOFLAGS}}' + lua__: 'lua' + LUA: '{{default .lua__ .LUA}}' tasks: default: @@ -24,6 +26,12 @@ tasks: vars: GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"' + midnight: + cmds: + - go build -tags midnight,{{.LUA}} {{.GOFLAGS}} + vars: + GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"' + build: cmds: - go build {{.GOFLAGS}} diff --git a/aliases.go b/aliases.go index 4b0eb8d..a6c46fe 100644 --- a/aliases.go +++ b/aliases.go @@ -97,10 +97,12 @@ func (a *aliasModule) Resolve(cmdstr string) string { func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table { // create a lua module with our functions hshaliasesLua := map[string]util.LuaExport{ - //"add": util.LuaExport{hlalias, 2, false}, + /* + "add": util.LuaExport{hlalias, 2, false}, "list": util.LuaExport{a.luaList, 0, false}, "del": util.LuaExport{a.luaDelete, 1, false}, "resolve": util.LuaExport{a.luaResolve, 1, false}, + */ } mod := rt.NewTable() diff --git a/api.go b/api.go index 73f8b3d..5abcb71 100644 --- a/api.go +++ b/api.go @@ -149,12 +149,12 @@ func getenv(key, fallback string) string { } func setVimMode(mode string) { - hshMod.SetField("vimMode", rt.StringValue(mode)) + hshMod.SetField("vimMode", moonlight.StringValue(mode)) hooks.Emit("hilbish.vimMode", mode) } func unsetVimMode() { - hshMod.SetField("vimMode", rt.NilValue) + hshMod.SetField("vimMode", moonlight.NilValue) } func handleStream(v rt.Value, strms *streams, errStream bool) error { diff --git a/editor.go b/editor.go index 9c49440..67248ff 100644 --- a/editor.go +++ b/editor.go @@ -12,11 +12,13 @@ import ( // directly interact with the line editor in use. func editorLoader(rtm *rt.Runtime) *rt.Table { exports := map[string]util.LuaExport{ + /* "insert": {editorInsert, 1, false}, "setVimRegister": {editorSetRegister, 1, false}, "getVimRegister": {editorGetRegister, 2, false}, "getLine": {editorGetLine, 0, false}, "readChar": {editorReadChar, 0, false}, + */ } mod := rt.NewTable() diff --git a/exec.go b/exec.go index 412b07a..0828e01 100644 --- a/exec.go +++ b/exec.go @@ -28,7 +28,7 @@ import ( var errNotExec = errors.New("not executable") var errNotFound = errors.New("not found") -var runnerMode rt.Value = rt.StringValue("hybrid") +var runnerMode moonlight.Value = moonlight.StringValue("hybrid") type streams struct { stdout io.Writer @@ -101,7 +101,7 @@ func runInput(input string, priv bool) { var cont bool // save incase it changes while prompting (For some reason) currentRunner := runnerMode - if currentRunner.Type() == rt.StringType { + if currentRunner.Type() == moonlight.StringType { switch currentRunner.AsString() { case "hybrid": _, _, err = handleLua(input) @@ -171,69 +171,41 @@ func reprompt(input string) (string, error) { } } -func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8, continued bool, runnerErr, err error) { +func runLuaRunner(runr moonlight.Value, userInput string) (input string, exitCode uint8, continued bool, runnerErr, err error) { runnerRet, err := l.Call1(runr, moonlight.StringValue(userInput)) if err != nil { return "", 124, false, nil, err } - var runner *rt.Table + var runner *moonlight.Table var ok bool - if runner, ok = runnerRet.TryTable(); !ok { + if runner, ok = moonlight.TryTable(runnerRet); !ok { fmt.Fprintln(os.Stderr, "runner did not return a table") exitCode = 125 input = userInput return } - if code, ok := runner.Get(rt.StringValue("exitCode")).TryInt(); ok { + if code, ok := runner.Get(moonlight.StringValue("exitCode")).TryInt(); ok { exitCode = uint8(code) } - if inp, ok := runner.Get(rt.StringValue("input")).TryString(); ok { + if inp, ok := runner.Get(moonlight.StringValue("input")).TryString(); ok { input = inp } - if errStr, ok := runner.Get(rt.StringValue("err")).TryString(); ok { + if errStr, ok := runner.Get(moonlight.StringValue("err")).TryString(); ok { runnerErr = fmt.Errorf("%s", errStr) } - if c, ok := runner.Get(rt.StringValue("continue")).TryBool(); ok { + if c, ok := runner.Get(moonlight.StringValue("continue")).TryBool(); ok { continued = c } return } -func handleLua(input string) (string, uint8, error) { - cmdString := aliases.Resolve(input) - // First try to load input, essentially compiling to bytecode - rtm := l.UnderlyingRuntime() - chunk, err := rtm.CompileAndLoadLuaChunk("", []byte(cmdString), moonlight.TableValue(l.GlobalTable())) - if err != nil && noexecute { - fmt.Println(err) - /* if lerr, ok := err.(*lua.ApiError); ok { - if perr, ok := lerr.Cause.(*parse.Error); ok { - print(perr.Pos.Line == parse.EOF) - } - } - */ - return cmdString, 125, err - } - // And if there's no syntax errors and -n isnt provided, run - if !noexecute { - if chunk != nil { - _, err = l.Call1(rt.FunctionValue(chunk)) - } - } - if err == nil { - return cmdString, 0, nil - } - - return cmdString, 125, err -} - func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr error) { - shRunner := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("sh")) + shRunner := hshMod.Get(moonlight.StringValue("runner")).AsTable().Get(moonlight.StringValue("sh")) var err error input, exitCode, cont, runErr, err = runLuaRunner(shRunner, cmdString) if err != nil { @@ -607,9 +579,9 @@ func splitInput(input string) ([]string, string) { } func cmdFinish(code uint8, cmdstr string, private bool) { - hshMod.SetField("exitCode", rt.IntValue(int64(code))) + hshMod.SetField("exitCode", moonlight.IntValue(int64(code))) // using AsValue (to convert to lua type) on an interface which is an int // results in it being unknown in lua .... ???? // so we allow the hook handler to take lua runtime Values - hooks.Emit("command.exit", rt.IntValue(int64(code)), cmdstr, private) + hooks.Emit("command.exit", moonlight.IntValue(int64(code)), cmdstr, private) } diff --git a/go.mod b/go.mod index 985f2e2..19cbc60 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 toolchain go1.22.2 require ( + github.com/aarzilli/golua v0.0.0-20210507130708-11106aa57765 github.com/arnodel/golua v0.0.0-20230215163904-e0b5347eaaa1 github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 github.com/blackfireio/osinfo v1.0.5 diff --git a/go.sum b/go.sum index 136f827..53f9b33 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749 h1:jIFnWBTsYw8s7RX7 github.com/Rosettea/golua v0.0.0-20240427174124-d239074c1749/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240720131751-805c301321fd h1:THNle0FR2g7DMO1y3Bx1Zr7rYeiLXt3st3UkxEsMzL4= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240720131751-805c301321fd/go.mod h1:YZalN5H7WNQw3DGij6IvHsEhn5YMW7M2FCwG6gnfKy4= +github.com/aarzilli/golua v0.0.0-20210507130708-11106aa57765 h1:N6gB4UCRBZz8twlJbMFiCKj0zX5Et2nFU/LRafT4x80= +github.com/aarzilli/golua v0.0.0-20210507130708-11106aa57765/go.mod h1:hMjfaJVSqVnxenMlsxrq3Ni+vrm9Hs64tU4M7dhUoO4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw= diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go index cbe58cd..b0e7bb2 100644 --- a/golibs/bait/bait.go +++ b/golibs/bait/bait.go @@ -82,16 +82,17 @@ func (b *Bait) Emit(event string, args ...interface{}) { }() if handle.typ == luaListener { - funcVal := rt.FunctionValue(handle.luaCaller) + //funcVal := moonlight.FunctionValue(handle.luaCaller) var luaArgs []moonlight.Value for _, arg := range args { var luarg moonlight.Value switch arg.(type) { case moonlight.Value: luarg = arg.(moonlight.Value) - default: luarg = rt.AsValue(arg) + default: luarg = moonlight.AsValue(arg) } luaArgs = append(luaArgs, luarg) } + /* _, err := b.rtm.Call1(funcVal, luaArgs...) if err != nil { if event != "error" { @@ -102,6 +103,7 @@ func (b *Bait) Emit(event string, args ...interface{}) { // (calls the go recoverer function) panic(err) } + */ } else { handle.caller(args...) } @@ -146,7 +148,7 @@ func (b *Bait) Off(event string, listener *Listener) { } // OffLua removes a Lua function handler for an event. -func (b *Bait) OffLua(event string, handler *rt.Closure) { +func (b *Bait) OffLua(event string, handler *moonlight.Closure) { handles := b.handlers[event] for i, handle := range handles { @@ -169,7 +171,7 @@ func (b *Bait) Once(event string, handler func(...interface{})) *Listener { } // OnceLua adds a Lua function listener for an event that only runs once. -func (b *Bait) OnceLua(event string, handler *rt.Closure) *Listener { +func (b *Bait) OnceLua(event string, handler *moonlight.Closure) *Listener { listener := &Listener{ typ: luaListener, once: true, diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go index 53d55f3..486db42 100644 --- a/golibs/commander/commander.go +++ b/golibs/commander/commander.go @@ -36,19 +36,17 @@ import ( "hilbish/moonlight" "hilbish/util" "hilbish/golibs/bait" - - rt "github.com/arnodel/golua/runtime" ) type Commander struct{ Events *bait.Bait - Commands map[string]*rt.Closure + Commands map[string]*moonlight.Closure } func New(rtm *moonlight.Runtime) *Commander { c := &Commander{ Events: bait.New(rtm), - Commands: make(map[string]*rt.Closure), + Commands: make(map[string]*moonlight.Closure), } return c @@ -119,7 +117,9 @@ func (c *Commander) cregistry(mlr *moonlight.Runtime, ct *moonlight.GoCont) (moo registryLua := moonlight.NewTable() for cmdName, cmd := range c.Commands { cmdTbl := moonlight.NewTable() - cmdTbl.SetField("exec", rt.FunctionValue(cmd)) + //cmdTbl.SetField("exec", moonlight.FunctionValue(cmd)) + print(cmd) + cmdTbl.SetField("exec", moonlight.StringValue("placeholder")) registryLua.SetField(cmdName, moonlight.TableValue(cmdTbl)) } diff --git a/golibs/fs/fs.go b/golibs/fs/fs.go index 61ad799..6b3b643 100644 --- a/golibs/fs/fs.go +++ b/golibs/fs/fs.go @@ -55,8 +55,8 @@ func (f *fs) Loader(rtm *moonlight.Runtime) moonlight.Value { mod := moonlight.NewTable() rtm.SetExports(mod, exports) - mod.SetField("pathSep", rt.StringValue(string(os.PathSeparator))) - mod.SetField("pathListSep", rt.StringValue(string(os.PathListSeparator))) + mod.SetField("pathSep", moonlight.StringValue(string(os.PathSeparator))) + mod.SetField("pathListSep", moonlight.StringValue(string(os.PathListSeparator))) return moonlight.TableValue(mod) } @@ -280,7 +280,7 @@ func (f *fs) freaddir(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Co return nil, err } for i, entry := range dirEntries { - names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name())) + names.Set(moonlight.IntValue(int64(i + 1)), moonlight.StringValue(entry.Name())) } return mlr.PushNext1(c, moonlight.TableValue(names)), nil diff --git a/golibs/terminal/terminal.go b/golibs/terminal/terminal.go index e1af46d..270d668 100644 --- a/golibs/terminal/terminal.go +++ b/golibs/terminal/terminal.go @@ -7,7 +7,6 @@ import ( "hilbish/moonlight" - rt "github.com/arnodel/golua/runtime" "golang.org/x/term" ) @@ -37,8 +36,8 @@ func termsize(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, erro } dimensions := moonlight.NewTable() - dimensions.Set(rt.StringValue("width"), rt.IntValue(int64(w))) - dimensions.Set(rt.StringValue("height"), rt.IntValue(int64(h))) + dimensions.SetField("width", moonlight.IntValue(int64(w))) + dimensions.SetField("height", moonlight.IntValue(int64(h))) return mlr.PushNext1(c, moonlight.TableValue(dimensions)), nil } diff --git a/history.go b/history.go index aab4466..83d7c5b 100644 --- a/history.go +++ b/history.go @@ -7,17 +7,17 @@ import ( "path/filepath" "strings" - rt "github.com/arnodel/golua/runtime" + "hilbish/moonlight" ) type luaHistory struct {} func (h *luaHistory) Write(line string) (int, error) { - histWrite := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("add")) - ln, err := l.Call1(histWrite, rt.StringValue(line)) + histWrite := hshMod.Get(moonlight.StringValue("history")).AsTable().Get(moonlight.StringValue("add")) + ln, err := l.Call1(histWrite, moonlight.StringValue(line)) var num int64 - if ln.Type() == rt.IntType { + if ln.Type() == moonlight.IntType { num = ln.AsInt() } @@ -25,11 +25,11 @@ func (h *luaHistory) Write(line string) (int, error) { } func (h *luaHistory) GetLine(idx int) (string, error) { - histGet := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("get")) - lcmd, err := l.Call1(histGet, rt.IntValue(int64(idx))) + histGet := hshMod.Get(moonlight.StringValue("history")).AsTable().Get(moonlight.StringValue("get")) + lcmd, err := l.Call1(histGet, moonlight.IntValue(int64(idx))) var cmd string - if lcmd.Type() == rt.StringType { + if lcmd.Type() == moonlight.StringType { cmd = lcmd.AsString() } @@ -37,11 +37,11 @@ func (h *luaHistory) GetLine(idx int) (string, error) { } func (h *luaHistory) Len() int { - histSize := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("size")) + histSize := hshMod.Get(moonlight.StringValue("history")).AsTable().Get(moonlight.StringValue("size")) ln, _ := l.Call1(histSize) var num int64 - if ln.Type() == rt.IntType { + if ln.Type() == moonlight.IntType { num = ln.AsInt() } diff --git a/job.go b/job.go index cf2615f..408619b 100644 --- a/job.go +++ b/job.go @@ -261,7 +261,7 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job { stdout: &bytes.Buffer{}, stderr: &bytes.Buffer{}, } - jb.ud = jobUserData(jb) + //jb.ud = jobUserData(jb) j.jobs[j.latestID] = jb hooks.Emit("job.add", rt.UserDataValue(jb.ud)) @@ -389,10 +389,12 @@ func valueToJob(val rt.Value) (*job, bool) { return j, ok } +/* func jobUserData(j *job) *rt.UserData { jobMeta := l.UnderlyingRuntime().Registry(jobMetaKey) return rt.NewUserData(j, jobMeta.AsTable()) } +*/ // #interface jobs // get(id) -> @Job diff --git a/lua_exec.go b/lua_exec.go new file mode 100644 index 0000000..ad279dd --- /dev/null +++ b/lua_exec.go @@ -0,0 +1,36 @@ +//go:build !midnight +package main + +import ( + "fmt" + + "hilbish/moonlight" +) + +func handleLua(input string) (string, uint8, error) { + cmdString := aliases.Resolve(input) + // First try to load input, essentially compiling to bytecode + rtm := l.UnderlyingRuntime() + chunk, err := rtm.CompileAndLoadLuaChunk("", []byte(cmdString), moonlight.TableValue(l.GlobalTable())) + if err != nil && noexecute { + fmt.Println(err) + /* if lerr, ok := err.(*lua.ApiError); ok { + if perr, ok := lerr.Cause.(*parse.Error); ok { + print(perr.Pos.Line == parse.EOF) + } + } + */ + return cmdString, 125, err + } + // And if there's no syntax errors and -n isnt provided, run + if !noexecute { + if chunk != nil { + _, err = l.Call1(rt.FunctionValue(chunk)) + } + } + if err == nil { + return cmdString, 0, nil + } + + return cmdString, 125, err +} diff --git a/lua_exec_midnight.go b/lua_exec_midnight.go new file mode 100644 index 0000000..85e57b7 --- /dev/null +++ b/lua_exec_midnight.go @@ -0,0 +1,10 @@ +//go:build midnight +package main + +import ( + "errors" +) + +func handleLua(input string) (string, uint8, error) { + return "", 7, errors.New("lua input in midnight placeholder") +} diff --git a/main.go b/main.go index 92e801c..723712e 100644 --- a/main.go +++ b/main.go @@ -170,12 +170,12 @@ func main() { } if getopt.NArgs() > 0 { - luaArgs := rt.NewTable() + luaArgs := moonlight.NewTable() for i, arg := range getopt.Args() { - luaArgs.Set(rt.IntValue(int64(i)), rt.StringValue(arg)) + luaArgs.Set(moonlight.IntValue(int64(i)), moonlight.StringValue(arg)) } - l.GlobalTable().SetField("args", rt.TableValue(luaArgs)) + l.GlobalTable().SetField("args", moonlight.TableValue(luaArgs)) err := l.DoFile(getopt.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/moonlight/closure_clua.go b/moonlight/closure_clua.go new file mode 100644 index 0000000..549c20a --- /dev/null +++ b/moonlight/closure_clua.go @@ -0,0 +1,28 @@ +//go:build midnight +package moonlight + +import ( + "fmt" +) + +type Callable interface{ + Continuation(*Runtime, Cont) Cont +} + +type Closure struct{ + refIdx int // so since we cant store the actual lua closure, + // we need a index to the ref in the lua registry... or something like that. +} + +func (mlr *Runtime) ClosureArg(c *GoCont, num int) (*Closure, error) { + fmt.Println("type at ", num, "is", mlr.state.LTypename(num)) + + return &Closure{ + refIdx: -1, + }, nil +} + +/* +func (c *Closure) Continuation(mlr *Runtime, c Cont) Cont { +} +*/ diff --git a/moonlight/cont_clua.go b/moonlight/cont_clua.go new file mode 100644 index 0000000..088b8c0 --- /dev/null +++ b/moonlight/cont_clua.go @@ -0,0 +1,12 @@ +//go:build midnight +package moonlight + +type GoCont struct{ + vals []Value + f GoFunctionFunc +} +type Cont interface{} + +func (gc *GoCont) Next() Cont { + return gc +} diff --git a/moonlight/cont_golua.go b/moonlight/cont_golua.go index d34993e..34cd858 100644 --- a/moonlight/cont_golua.go +++ b/moonlight/cont_golua.go @@ -1,3 +1,4 @@ +//go:build !midnight package moonlight import ( @@ -8,6 +9,7 @@ type GoCont struct{ cont *rt.GoCont thread *rt.Thread } + type Cont = rt.Cont type Closure = rt.Closure diff --git a/moonlight/export.go b/moonlight/export.go new file mode 100644 index 0000000..2567af5 --- /dev/null +++ b/moonlight/export.go @@ -0,0 +1,7 @@ +package moonlight + +type Export struct{ + Function GoToLuaFunc + ArgNum int + Variadic bool +} diff --git a/moonlight/export_clua.go b/moonlight/export_clua.go new file mode 100644 index 0000000..8857392 --- /dev/null +++ b/moonlight/export_clua.go @@ -0,0 +1,8 @@ +//go:build midnight +package moonlight + +func (mlr *Runtime) SetExports(tbl *Table, exports map[string]Export) { + for name, export := range exports { + tbl.SetField(name, FunctionValue(mlr.GoFunction(export.Function))) + } +} diff --git a/moonlight/export_golua.go b/moonlight/export_golua.go index 426d6a8..50efdea 100644 --- a/moonlight/export_golua.go +++ b/moonlight/export_golua.go @@ -1,11 +1,6 @@ +//go:build !midnight package moonlight -type Export struct{ - Function GoToLuaFunc - ArgNum int - Variadic bool -} - func (mlr *Runtime) SetExports(tbl *Table, exports map[string]Export) { for name, export := range exports { mlr.rt.SetEnvGoFunc(tbl.lt, name, mlr.GoFunction(export.Function), export.ArgNum, export.Variadic) diff --git a/moonlight/function_clua.go b/moonlight/function_clua.go new file mode 100644 index 0000000..ee3d482 --- /dev/null +++ b/moonlight/function_clua.go @@ -0,0 +1,61 @@ +//go:build midnight +package moonlight + +import ( + "fmt" + + "github.com/aarzilli/golua/lua" +) + +type GoFunctionFunc struct{ + cf lua.LuaGoFunction +} + +func (gf GoFunctionFunc) Continuation(mlr *Runtime, c Cont) Cont { + return &GoCont{ + f: gf, + vals: []Value{}, + } +} + +func (mlr *Runtime) CheckNArgs(c *GoCont, num int) error { + args := mlr.state.GetTop() + if args < num { + return fmt.Errorf("%d arguments needed", num) + } + + return nil +} + +func (mlr *Runtime) Check1Arg(c *GoCont) error { + return mlr.CheckNArgs(c, 1) +} + +func (mlr *Runtime) StringArg(c *GoCont, num int) (string, error) { + return mlr.state.CheckString(num + 1), nil +} + +func (mlr *Runtime) Arg(c *GoCont, num int) Value { + return c.vals[num] +} + +func (mlr *Runtime) GoFunction(fun GoToLuaFunc) GoFunctionFunc { + return GoFunctionFunc{ + cf: func(L *lua.State) int { + cont, err := fun(mlr, &GoCont{}) + if err != nil { + L.RaiseError(err.Error()) + return 0 + } + + for _, val := range cont.(*GoCont).vals { + switch Type(val) { + case StringType: + L.PushString(val.AsString()) + } + } + + return len(cont.(*GoCont).vals) + }, + } +} diff --git a/moonlight/function_golua.go b/moonlight/function_golua.go index 5597968..9f9b05f 100644 --- a/moonlight/function_golua.go +++ b/moonlight/function_golua.go @@ -1,3 +1,4 @@ +//go:build !midnight package moonlight import ( @@ -26,7 +27,7 @@ func (mlr *Runtime) Arg(c *GoCont, num int) Value { return c.cont.Arg(num) } -func (mlr *Runtime) GoFunction(fun GoToLuaFunc) rt.GoFunctionFunc { +func (mlr *Runtime) GoFunction(fun GoToLuaFunc) GoFunctionFunc { return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { gocont := GoCont{ cont: c, @@ -35,7 +36,3 @@ func (mlr *Runtime) GoFunction(fun GoToLuaFunc) rt.GoFunctionFunc { return fun(mlr, &gocont) } } - -func (mlr *Runtime) Call1(val Value, args ...Value) (Value, error) { - return rt.Call1(mlr.rt.MainThread(), val, args...) -} diff --git a/moonlight/loader_clua.go b/moonlight/loader_clua.go new file mode 100644 index 0000000..6f3a601 --- /dev/null +++ b/moonlight/loader_clua.go @@ -0,0 +1,21 @@ +//go:build midnight +package moonlight + +import ( + "github.com/aarzilli/golua/lua" +) + +type Loader func(*Runtime) Value + +func (mlr *Runtime) LoadLibrary(ldr Loader, name string) { + cluaLoader := func (L *lua.State) int { + mlr.pushToState(ldr(mlr)) + + return 1 + } + + mlr.state.GetGlobal("package") + mlr.state.GetField(-1, "preload") + mlr.state.PushGoFunction(cluaLoader) + mlr.state.SetField(-2, name) +} diff --git a/moonlight/loader_golua.go b/moonlight/loader_golua.go index e2d505f..9560021 100644 --- a/moonlight/loader_golua.go +++ b/moonlight/loader_golua.go @@ -1,3 +1,4 @@ +//go:build !midnight package moonlight import ( diff --git a/moonlight/runtime_clua.go b/moonlight/runtime_clua.go new file mode 100644 index 0000000..89b4643 --- /dev/null +++ b/moonlight/runtime_clua.go @@ -0,0 +1,49 @@ +//go:build midnight +package moonlight + +import ( + "github.com/aarzilli/golua/lua" +) + +type Runtime struct{ + state *lua.State +} + +func NewRuntime() *Runtime { + L := lua.NewState() + L.OpenLibs() + + return &Runtime{ + state: L, + } +} + +func (mlr *Runtime) PushNext1(c *GoCont, v Value) Cont { + c.vals = []Value{v} + + return c +} + +func (mlr *Runtime) Call1(f Value, args ...Value) (Value, error) { + for _, arg := range args { + mlr.pushToState(arg) + } + + if f.refIdx > 0 { + mlr.state.RawGeti(lua.LUA_REGISTRYINDEX, f.refIdx) + mlr.state.Call(len(args), 1) + } + + if mlr.state.GetTop() == 0 { + return NilValue, nil + } + + return NilValue, nil +} + +func (mlr *Runtime) pushToState(v Value) { + switch v.Type() { + case NilType: mlr.state.PushNil() + case StringType: mlr.state.PushString(v.AsString()) + } +} diff --git a/moonlight/runtime_golua.go b/moonlight/runtime_golua.go index 095931f..57c90d0 100644 --- a/moonlight/runtime_golua.go +++ b/moonlight/runtime_golua.go @@ -1,3 +1,4 @@ +//go:build !midnight package moonlight import ( @@ -42,3 +43,7 @@ func (mlr *Runtime) Push(c *GoCont, v Value) { func (mlr *Runtime) PushNext1(c *GoCont, v Value) Cont { return c.cont.PushingNext1(c.thread.Runtime, v) } + +func (mlr *Runtime) Call1(val Value, args ...Value) (Value, error) { + return rt.Call1(mlr.rt.MainThread(), val, args...) +} diff --git a/moonlight/table_clua.go b/moonlight/table_clua.go new file mode 100644 index 0000000..4533553 --- /dev/null +++ b/moonlight/table_clua.go @@ -0,0 +1,43 @@ +//go:build midnight +package moonlight + +//import "github.com/aarzilli/golua/lua" + +type Table struct{ + refIdx int +} + +func NewTable() *Table { + return &Table{ + refIdx: -1, + } +} + +func (t *Table) Get(val Value) Value { + return NilValue +} + +func (t *Table) SetField(key string, value Value) { +} + +func (t *Table) Set(key Value, value Value) { +} + +func ForEach(tbl *Table, cb func(key Value, val Value)) { +} + +func (mlr *Runtime) GlobalTable() *Table { + return &Table{ + refIdx: -1, + } +} + +func ToTable(v Value) *Table { + return &Table{ + refIdx: -1, + } +} + +func TryTable(v Value) (*Table, bool) { + return nil, false +} diff --git a/moonlight/table_golua.go b/moonlight/table_golua.go index 8c0238f..b78d88a 100644 --- a/moonlight/table_golua.go +++ b/moonlight/table_golua.go @@ -1,3 +1,4 @@ +//go:build !midnight package moonlight import ( @@ -50,3 +51,9 @@ func convertToMoonlightTable(t *rt.Table) *Table { lt: t, } } + +func TryTable(v Value) (*Table, bool) { + t, ok := v.TryTable() + + return convertToMoonlightTable(t), ok +} diff --git a/moonlight/util_clua.go b/moonlight/util_clua.go new file mode 100644 index 0000000..aa60f0e --- /dev/null +++ b/moonlight/util_clua.go @@ -0,0 +1,12 @@ +//go:build midnight +package moonlight + +func (mlr *Runtime) DoString(code string) (Value, error) { + err := mlr.state.DoString(code) + + return NilValue, err +} + +func (mlr *Runtime) DoFile(filename string) error { + return mlr.state.DoFile(filename) +} diff --git a/moonlight/util_golua.go b/moonlight/util_golua.go index bc0a440..64e61cd 100644 --- a/moonlight/util_golua.go +++ b/moonlight/util_golua.go @@ -1,3 +1,4 @@ +//go:build !midnight package moonlight import ( @@ -9,7 +10,7 @@ import ( ) // DoString runs the code string in the Lua runtime. -func (mlr *Runtime) DoString(code string) (rt.Value, error) { +func (mlr *Runtime) DoString(code string) (Value, error) { chunk, err := mlr.rt.CompileAndLoadLuaChunk("", []byte(code), rt.TableValue(mlr.rt.GlobalEnv())) var ret rt.Value if chunk != nil { diff --git a/moonlight/value_clua.go b/moonlight/value_clua.go new file mode 100644 index 0000000..1662851 --- /dev/null +++ b/moonlight/value_clua.go @@ -0,0 +1,121 @@ +//go:build midnight +package moonlight + +type Value struct{ + iface interface{} + relIdx int + refIdx int +} + +var NilValue = Value{nil, -1, -1} + +type ValueType uint8 +const ( + NilType ValueType = iota + BoolType + IntType + StringType + TableType + FunctionType + UnknownType +) + +func Type(v Value) ValueType { + return v.Type() +} + +func BoolValue(b bool) Value { + return Value{iface: b} +} + +func IntValue(i int64) Value { + return Value{iface: i} +} + +func StringValue(str string) Value { + return Value{iface: str} +} + +func TableValue(t *Table) Value { + return Value{iface: t} +} + +func FunctionValue(f Callable) Value { + return NilValue +} + +func AsValue(i interface{}) Value { + if i == nil { + return NilValue + } + + switch v := i.(type) { + case bool: return BoolValue(v) + case int64: return IntValue(v) + case string: return StringValue(v) + case *Table: return TableValue(v) + case Value: return v + default: + return Value{iface: i} + } +} + +func (v Value) Type() ValueType { + if v.iface == nil { + return NilType + } + + switch v.iface.(type) { + case bool: return BoolType + case int: return IntType + case string: return StringType + case *Table: return TableType + case *Closure: return FunctionType + default: return UnknownType + } +} + +func (v Value) AsInt() int64 { + return v.iface.(int64) +} + +func (v Value) AsString() string { + if v.Type() != StringType { + panic("value type was not string") + } + + return v.iface.(string) +} + +func (v Value) AsTable() *Table { + panic("Value.AsTable unimplemented in midnight") +} + +func ToString(v Value) string { + return v.AsString() +} + +func (v Value) TypeName() string { + switch v.iface.(type) { + case bool: return "bool" + case int: return "number" + case string: return "string" + case *Table: return "table" + default: return "" + } +} + +func (v Value) TryBool() (n bool, ok bool) { + n, ok = v.iface.(bool) + return +} + +func (v Value) TryInt() (n int, ok bool) { + n, ok = v.iface.(int) + return +} + +func (v Value) TryString() (n string, ok bool) { + n, ok = v.iface.(string) + return +} diff --git a/moonlight/value_golua.go b/moonlight/value_golua.go index 2c74394..eae19f9 100644 --- a/moonlight/value_golua.go +++ b/moonlight/value_golua.go @@ -1,17 +1,25 @@ +//go:build !midnight package moonlight import ( rt "github.com/arnodel/golua/runtime" ) +var NilValue = rt.NilValue + type Value = rt.Value type ValueType = rt.ValueType const ( + IntType = rt.IntType StringType = rt.StringType FunctionType = rt.FunctionType TableType = rt.TableType ) +func Type(v Value) ValueType { + return ValueType(v.Type()) +} + func StringValue(str string) Value { return rt.StringValue(str) } @@ -28,10 +36,6 @@ func TableValue(t *Table) Value { return rt.TableValue(t.lt) } -func Type(v Value) ValueType { - return ValueType(v.Type()) -} - func ToString(v Value) string { return v.AsString() } @@ -39,3 +43,7 @@ func ToString(v Value) string { func ToTable(v Value) *Table { return convertToMoonlightTable(v.AsTable()) } + +func AsValue(v interface{}) Value { + return rt.AsValue(v) +} diff --git a/os.go b/os.go index 4738d9b..b2f8530 100644 --- a/os.go +++ b/os.go @@ -4,7 +4,6 @@ import ( "hilbish/moonlight" //"hilbish/util" - rt "github.com/arnodel/golua/runtime" "github.com/blackfireio/osinfo" ) @@ -19,9 +18,9 @@ func hshosLoader() *moonlight.Table { info, _ := osinfo.GetOSInfo() mod := moonlight.NewTable() - mod.SetField("family", rt.StringValue(info.Family)) - mod.SetField("name", rt.StringValue(info.Name)) - mod.SetField("version", rt.StringValue(info.Version)) + mod.SetField("family", moonlight.StringValue(info.Family)) + mod.SetField("name", moonlight.StringValue(info.Name)) + mod.SetField("version", moonlight.StringValue(info.Version)) return mod } diff --git a/rl.go b/rl.go index 28db1f6..50151b1 100644 --- a/rl.go +++ b/rl.go @@ -5,6 +5,7 @@ import ( "io" "strings" + "hilbish/moonlight" "hilbish/util" rt "github.com/arnodel/golua/runtime" @@ -70,8 +71,8 @@ func newLineReader(prompt string, noHist bool) *lineReader { hooks.Emit("hilbish.vimAction", actionStr, args) } rl.HintText = func(line []rune, pos int) []rune { - hinter := hshMod.Get(rt.StringValue("hinter")) - retVal, err := l.Call1(hinter, rt.StringValue(string(line)), rt.IntValue(int64(pos))) + hinter := hshMod.Get(moonlight.StringValue("hinter")) + retVal, err := l.Call1(hinter, moonlight.StringValue(string(line)), moonlight.IntValue(int64(pos))) if err != nil { fmt.Println(err) return []rune{} @@ -85,8 +86,8 @@ func newLineReader(prompt string, noHist bool) *lineReader { return []rune(hintText) } rl.SyntaxHighlighter = func(line []rune) string { - highlighter := hshMod.Get(rt.StringValue("highlighter")) - retVal, err := l.Call1(highlighter, rt.StringValue(string(line))) + highlighter := hshMod.Get(moonlight.StringValue("highlighter")) + retVal, err := l.Call1(highlighter, moonlight.StringValue(string(line))) if err != nil { fmt.Println(err) return string(line) @@ -99,93 +100,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { return highlighted } - rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) { - term := rt.NewTerminationWith(l.UnderlyingRuntime().MainThread().CurrentCont(), 2, false) - compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler")) - err := rt.Call(l.UnderlyingRuntime().MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)), - rt.IntValue(int64(pos))}, term) - - var compGroups []*readline.CompletionGroup - if err != nil { - return "", compGroups - } - - luaCompGroups := term.Get(0) - luaPrefix := term.Get(1) - - if luaCompGroups.Type() != rt.TableType { - return "", compGroups - } - - groups := luaCompGroups.AsTable() - // prefix is optional - pfx, _ := luaPrefix.TryString() - - util.ForEach(groups, func(key rt.Value, val rt.Value) { - if key.Type() != rt.IntType || val.Type() != rt.TableType { - return - } - - valTbl := val.AsTable() - luaCompType := valTbl.Get(rt.StringValue("type")) - luaCompItems := valTbl.Get(rt.StringValue("items")) - - if luaCompType.Type() != rt.StringType || luaCompItems.Type() != rt.TableType { - return - } - - items := []string{} - itemDescriptions := make(map[string]string) - - util.ForEach(luaCompItems.AsTable(), func(lkey rt.Value, lval rt.Value) { - if keytyp := lkey.Type(); keytyp == rt.StringType { - // ['--flag'] = {'description', '--flag-alias'} - itemName, ok := lkey.TryString() - vlTbl, okk := lval.TryTable() - if !ok && !okk { - // TODO: error - return - } - - items = append(items, itemName) - itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString() - if !ok { - // TODO: error - return - } - itemDescriptions[itemName] = itemDescription - } else if keytyp == rt.IntType { - vlStr, ok := lval.TryString() - if !ok { - // TODO: error - return - } - items = append(items, vlStr) - } else { - // TODO: error - return - } - }) - - var dispType readline.TabDisplayType - switch luaCompType.AsString() { - case "grid": dispType = readline.TabDisplayGrid - case "list": dispType = readline.TabDisplayList - // need special cases, will implement later - //case "map": dispType = readline.TabDisplayMap - } - - compGroups = append(compGroups, &readline.CompletionGroup{ - DisplayType: dispType, - Descriptions: itemDescriptions, - Suggestions: items, - TrimSlash: false, - NoSpace: true, - }) - }) - - return pfx, compGroups - } + setupTabCompleter(rl) return lr } @@ -244,11 +159,13 @@ func (lr *lineReader) Resize() { // method of saving history. func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { lrLua := map[string]util.LuaExport{ + /* "add": {lr.luaAddHistory, 1, false}, "all": {lr.luaAllHistory, 0, false}, "clear": {lr.luaClearHistory, 0, false}, "get": {lr.luaGetHistory, 1, false}, "size": {lr.luaSize, 0, false}, + */ } mod := rt.NewTable() diff --git a/rl_midnight.go b/rl_midnight.go new file mode 100644 index 0000000..25fdfda --- /dev/null +++ b/rl_midnight.go @@ -0,0 +1,10 @@ +//go:build midnight +package main + +import ( + "github.com/maxlandon/readline" +) + +func setupTabCompleter(rl *readline.Instance) { + // TODO +} diff --git a/rl_notmidnight.go b/rl_notmidnight.go new file mode 100644 index 0000000..0b1e8d3 --- /dev/null +++ b/rl_notmidnight.go @@ -0,0 +1,97 @@ +//go:build !midnight +package main + +import ( + rt "github.com/arnodel/golua/runtime" + "github.com/maxlandon/readline" +) + +func setupTabCompleter(rl *readline.Instance) { + rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) { + term := rt.NewTerminationWith(l.UnderlyingRuntime().MainThread().CurrentCont(), 2, false) + compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler")) + err := rt.Call(l.UnderlyingRuntime().MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)), + rt.IntValue(int64(pos))}, term) + + var compGroups []*readline.CompletionGroup + if err != nil { + return "", compGroups + } + + luaCompGroups := term.Get(0) + luaPrefix := term.Get(1) + + if luaCompGroups.Type() != rt.TableType { + return "", compGroups + } + + groups := luaCompGroups.AsTable() + // prefix is optional + pfx, _ := luaPrefix.TryString() + + util.ForEach(groups, func(key rt.Value, val rt.Value) { + if key.Type() != rt.IntType || val.Type() != rt.TableType { + return + } + + valTbl := val.AsTable() + luaCompType := valTbl.Get(rt.StringValue("type")) + luaCompItems := valTbl.Get(rt.StringValue("items")) + + if luaCompType.Type() != rt.StringType || luaCompItems.Type() != rt.TableType { + return + } + + items := []string{} + itemDescriptions := make(map[string]string) + + util.ForEach(luaCompItems.AsTable(), func(lkey rt.Value, lval rt.Value) { + if keytyp := lkey.Type(); keytyp == rt.StringType { + // ['--flag'] = {'description', '--flag-alias'} + itemName, ok := lkey.TryString() + vlTbl, okk := lval.TryTable() + if !ok && !okk { + // TODO: error + return + } + + items = append(items, itemName) + itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString() + if !ok { + // TODO: error + return + } + itemDescriptions[itemName] = itemDescription + } else if keytyp == rt.IntType { + vlStr, ok := lval.TryString() + if !ok { + // TODO: error + return + } + items = append(items, vlStr) + } else { + // TODO: error + return + } + }) + + var dispType readline.TabDisplayType + switch luaCompType.AsString() { + case "grid": dispType = readline.TabDisplayGrid + case "list": dispType = readline.TabDisplayList + // need special cases, will implement later + //case "map": dispType = readline.TabDisplayMap + } + + compGroups = append(compGroups, &readline.CompletionGroup{ + DisplayType: dispType, + Descriptions: itemDescriptions, + Suggestions: items, + TrimSlash: false, + NoSpace: true, + }) + }) + + return pfx, compGroups + } +} diff --git a/runnermode.go b/runnermode.go index 3394877..269831d 100644 --- a/runnermode.go +++ b/runnermode.go @@ -2,8 +2,6 @@ package main import ( "hilbish/moonlight" - - rt "github.com/arnodel/golua/runtime" ) // #interface runner @@ -86,13 +84,13 @@ func shRunner(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, erro } _, exitCode, cont, err := execSh(aliases.Resolve(cmd)) - var luaErr moonlight.Value = rt.NilValue + var luaErr moonlight.Value = moonlight.NilValue if err != nil { luaErr = moonlight.StringValue(err.Error()) } runnerRet := moonlight.NewTable() runnerRet.SetField("input", moonlight.StringValue(cmd)) - runnerRet.SetField("exitCode", moonlight.IntValue(int(exitCode))) + runnerRet.SetField("exitCode", moonlight.IntValue(int64(exitCode))) runnerRet.SetField("continue", moonlight.BoolValue(cont)) runnerRet.SetField("err", luaErr) @@ -114,13 +112,13 @@ func luaRunner(mlr *moonlight.Runtime, c *moonlight.GoCont) (moonlight.Cont, err } input, exitCode, err := handleLua(cmd) - var luaErr moonlight.Value = rt.NilValue + var luaErr moonlight.Value = moonlight.NilValue if err != nil { luaErr = moonlight.StringValue(err.Error()) } runnerRet := moonlight.NewTable() runnerRet.SetField("input", moonlight.StringValue(input)) - runnerRet.SetField("exitCode", moonlight.IntValue(int(exitCode))) + runnerRet.SetField("exitCode", moonlight.IntValue(int64(exitCode))) runnerRet.SetField("err", luaErr) diff --git a/sink.go b/sink.go index 5934533..ec52634 100644 --- a/sink.go +++ b/sink.go @@ -220,7 +220,7 @@ func newSinkInput(r io.Reader) *sink { s := &sink{ reader: bufio.NewReader(r), } - s.ud = sinkUserData(s) + //s.ud = sinkUserData(s) if f, ok := r.(*os.File); ok { s.file = f @@ -234,7 +234,7 @@ func newSinkOutput(w io.Writer) *sink { writer: bufio.NewWriter(w), autoFlush: true, } - s.ud = sinkUserData(s) + //s.ud = sinkUserData(s) return s } @@ -258,7 +258,9 @@ func valueToSink(val rt.Value) (*sink, bool) { return s, ok } +/* func sinkUserData(s *sink) *rt.UserData { sinkMeta := l.UnderlyingRuntime().Registry(sinkMetaKey) return rt.NewUserData(s, sinkMeta.AsTable()) } +*/ diff --git a/timer.go b/timer.go index 5c1fa0d..68f85ed 100644 --- a/timer.go +++ b/timer.go @@ -2,10 +2,12 @@ package main import ( "errors" - "fmt" - "os" +// "fmt" +// "os" "time" +// "hilbish/moonlight" + rt "github.com/arnodel/golua/runtime" ) @@ -47,7 +49,8 @@ func (t *timer) start() error { for { select { case <-t.ticker.C: - _, err := l.Call1(rt.FunctionValue(t.fun)) + /* + _, err := l.Call1(moonlight.FunctionValue(t.fun)) if err != nil { fmt.Fprintln(os.Stderr, "Error in function:\n", err) t.stop() @@ -56,6 +59,7 @@ func (t *timer) start() error { if t.typ == timerTimeout { t.stop() } + */ case <-t.channel: t.ticker.Stop() return diff --git a/timerhandler.go b/timerhandler.go index c4cfc61..0a981d3 100644 --- a/timerhandler.go +++ b/timerhandler.go @@ -48,7 +48,7 @@ func (th *timersModule) create(typ timerType, dur time.Duration, fun *rt.Closure th: th, id: th.latestID, } - t.ud = timerUserData(t) + //t.ud = timerUserData(t) th.timers[th.latestID] = t @@ -144,6 +144,7 @@ func (th *timersModule) loader() *moonlight.Table { } l.SetExports(timerMethods, timerFuncs) +/* timerMeta := rt.NewTable() timerIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { ti, _ := timerArg(c, 0) @@ -168,6 +169,7 @@ func (th *timersModule) loader() *moonlight.Table { timerMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(timerIndex, "__index", 2, false))) l.UnderlyingRuntime().SetRegistry(timerMetaKey, rt.TableValue(timerMeta)) +*/ thExports := map[string]moonlight.Export{ /* @@ -179,8 +181,8 @@ func (th *timersModule) loader() *moonlight.Table { luaTh := moonlight.NewTable() l.SetExports(luaTh, thExports) - luaTh.SetField("INTERVAL", rt.IntValue(0)) - luaTh.SetField("TIMEOUT", rt.IntValue(1)) + luaTh.SetField("INTERVAL", moonlight.IntValue(0)) + luaTh.SetField("TIMEOUT", moonlight.IntValue(1)) return luaTh } @@ -204,7 +206,9 @@ func valueToTimer(val rt.Value) (*timer, bool) { return j, ok } +/* func timerUserData(j *timer) *rt.UserData { timerMeta := l.UnderlyingRuntime().Registry(timerMetaKey) return rt.NewUserData(j, timerMeta.AsTable()) } +*/ diff --git a/userdir.go b/userdir.go index 3ccd9ee..d4cbd39 100644 --- a/userdir.go +++ b/userdir.go @@ -2,9 +2,6 @@ package main import ( "hilbish/moonlight" - //"hilbish/util" - - rt "github.com/arnodel/golua/runtime" ) // #interface userDir @@ -17,8 +14,8 @@ import ( func userDirLoader() *moonlight.Table { mod := moonlight.NewTable() - mod.SetField("config", rt.StringValue(confDir)) - mod.SetField("data", rt.StringValue(userDataDir)) + mod.SetField("config", moonlight.StringValue(confDir)) + mod.SetField("data", moonlight.StringValue(userDataDir)) return mod } diff --git a/util/export.go b/util/export.go index 9cb40de..a4bd277 100644 --- a/util/export.go +++ b/util/export.go @@ -15,7 +15,9 @@ type LuaExport struct { // SetExports puts the Lua function exports in the table. func SetExports(rtm *rt.Runtime, tbl *rt.Table, exports map[string]LuaExport) { + /* for name, export := range exports { rtm.SetEnvGoFunc(tbl, name, export.Function, export.ArgNum, export.Variadic) } + */ } From 29ac92dc0b2e621e48334f2dd8646098900c7de8 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sun, 21 Jul 2024 11:40:13 -0400 Subject: [PATCH 21/24] fix: int64/int mixup, missing imports --- lua_exec.go | 1 + moonlight/value_golua.go | 4 ++-- rl_notmidnight.go | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lua_exec.go b/lua_exec.go index ad279dd..8fca346 100644 --- a/lua_exec.go +++ b/lua_exec.go @@ -5,6 +5,7 @@ import ( "fmt" "hilbish/moonlight" + rt "github.com/arnodel/golua/runtime" ) func handleLua(input string) (string, uint8, error) { diff --git a/moonlight/value_golua.go b/moonlight/value_golua.go index eae19f9..5989f96 100644 --- a/moonlight/value_golua.go +++ b/moonlight/value_golua.go @@ -24,8 +24,8 @@ func StringValue(str string) Value { return rt.StringValue(str) } -func IntValue(i int) Value { - return rt.IntValue(int64(i)) +func IntValue(i int64) Value { + return rt.IntValue(i) } func BoolValue(b bool) Value { diff --git a/rl_notmidnight.go b/rl_notmidnight.go index 0b1e8d3..219439f 100644 --- a/rl_notmidnight.go +++ b/rl_notmidnight.go @@ -2,6 +2,8 @@ package main import ( + "hilbish/util" + rt "github.com/arnodel/golua/runtime" "github.com/maxlandon/readline" ) From 983605a5f7a47691014de6261b9919b1138a592e Mon Sep 17 00:00:00 2001 From: sammyette Date: Sun, 21 Jul 2024 12:18:37 -0400 Subject: [PATCH 22/24] ci: make builds between midnight and standard more distinct --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efb2fa1..8d8d8df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,12 +33,12 @@ jobs: go-version: '1.22.2' - name: Download Task run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d' - - name: Build + - name: Build ${{ matrix.midnight == 'true' && ' (Midnight Edition)' || ''}} run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task ${{ matrix.midnight == 'true' && 'midnight' || ''}} - uses: actions/upload-artifact@v4 if: matrix.goos == 'windows' with: - name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }} + name: hilbish${{ matrix.midnight == 'true' && '-midnight-edition' || ''}}-${{ matrix.goos }}-${{ matrix.goarch }} path: | hilbish.exe LICENSE @@ -52,7 +52,7 @@ jobs: - uses: actions/upload-artifact@v4 if: matrix.goos != 'windows' with: - name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }} + name: hilbish${{ matrix.midnight == 'true' && '-midnight-edition' || ''}}-${{ matrix.goos }}-${{ matrix.goarch }} path: | hilbish LICENSE From 2eb1ea398ff037fae3b3a6eb0e7e25fc70dc3bc5 Mon Sep 17 00:00:00 2001 From: sammyette Date: Sun, 21 Jul 2024 12:24:10 -0400 Subject: [PATCH 23/24] ci: add midnight edition on build name --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d8d8df..d0b59b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: jobs: build: - name: ${{ matrix.goos }}-${{ matrix.goarch }} + name: ${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.midnight == 'true' && ' (Midnight Edition)' || ''}} runs-on: ubuntu-latest strategy: matrix: @@ -33,7 +33,7 @@ jobs: go-version: '1.22.2' - name: Download Task run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d' - - name: Build ${{ matrix.midnight == 'true' && ' (Midnight Edition)' || ''}} + - name: Build run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task ${{ matrix.midnight == 'true' && 'midnight' || ''}} - uses: actions/upload-artifact@v4 if: matrix.goos == 'windows' From d6eb92f321d78a5e7f39a8f3efe15bbf833188fd Mon Sep 17 00:00:00 2001 From: sammyette Date: Fri, 26 Jul 2024 17:58:25 -0400 Subject: [PATCH 24/24] fix: attempt some small fixes for loading modules --- api.go | 1 + lua.go | 2 +- moonlight/loader_clua.go | 2 +- moonlight/table_clua.go | 4 ++++ moonlight/util_clua.go | 3 ++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api.go b/api.go index 5abcb71..aaf4dfa 100644 --- a/api.go +++ b/api.go @@ -37,6 +37,7 @@ import ( var hshMod *moonlight.Table func hilbishLoader(mlr *moonlight.Runtime) moonlight.Value { + println("hilbish loader called") var exports = map[string]moonlight.Export{ "alias": {hlalias, 2, false}, "appendPath": {hlappendPath, 1, false}, diff --git a/lua.go b/lua.go index 9e05af1..b0a584a 100644 --- a/lua.go +++ b/lua.go @@ -52,7 +52,7 @@ func luaInit() { */ // Add more paths that Lua can require from - _, err := l.DoString("package.path = package.path .. " + requirePaths) + _, err := l.DoString("print(type(hilbish)); package.path = package.path .. " + requirePaths) if err != nil { fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.") diff --git a/moonlight/loader_clua.go b/moonlight/loader_clua.go index 6f3a601..0bb2c17 100644 --- a/moonlight/loader_clua.go +++ b/moonlight/loader_clua.go @@ -15,7 +15,7 @@ func (mlr *Runtime) LoadLibrary(ldr Loader, name string) { } mlr.state.GetGlobal("package") - mlr.state.GetField(-1, "preload") + mlr.state.GetField(-1, "loaded") mlr.state.PushGoFunction(cluaLoader) mlr.state.SetField(-2, name) } diff --git a/moonlight/table_clua.go b/moonlight/table_clua.go index 4533553..75cee49 100644 --- a/moonlight/table_clua.go +++ b/moonlight/table_clua.go @@ -41,3 +41,7 @@ func ToTable(v Value) *Table { func TryTable(v Value) (*Table, bool) { return nil, false } + +func (t *Table) setRefIdx(mlr *Runtime, i idx) { + t.refIdx = mlr.state.Ref(i) +} diff --git a/moonlight/util_clua.go b/moonlight/util_clua.go index aa60f0e..2729cbb 100644 --- a/moonlight/util_clua.go +++ b/moonlight/util_clua.go @@ -8,5 +8,6 @@ func (mlr *Runtime) DoString(code string) (Value, error) { } func (mlr *Runtime) DoFile(filename string) error { - return mlr.state.DoFile(filename) + //return mlr.state.DoFile(filename) + return nil }