diff --git a/api.go b/api.go index 315884c8..8800f723 100644 --- a/api.go +++ b/api.go @@ -25,30 +25,31 @@ import ( "hilbish/util" - rt "github.com/arnodel/golua/runtime" "github.com/arnodel/golua/lib/packagelib" + rt "github.com/arnodel/golua/runtime" + //"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}, - "goro": {hlgoro, 1, true}, + "alias": {hlalias, 2, false}, + "appendPath": {hlappendPath, 1, false}, + "complete": {hlcomplete, 2, false}, + "cwd": {hlcwd, 0, false}, + "exec": {hlexec, 1, false}, + "goro": {hlgoro, 1, true}, "highlighter": {hlhighlighter, 1, false}, - "hinter": {hlhinter, 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}, - "timeout": {hltimeout, 2, false}, - "which": {hlwhich, 1, false}, + "prompt": {hlprompt, 1, true}, + "inputMode": {hlinputMode, 1, false}, + "interval": {hlinterval, 2, false}, + "read": {hlread, 1, false}, + "timeout": {hltimeout, 2, false}, + "which": {hlwhich, 1, false}, } var hshMod *rt.Table @@ -61,7 +62,9 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { mod := rt.NewTable() util.SetExports(rtm, mod, exports) - hshMod = mod + if hshMod == nil { + hshMod = mod + } host, _ := os.Hostname() username := curuser.Username @@ -131,18 +134,18 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { pluginModule := moduleLoader(rtm) mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule)) - sinkModule := util.SinkLoader(l) + sinkModule := util.SinkLoader(rtm) mod.Set(rt.StringValue("sink"), rt.TableValue(sinkModule)) return rt.TableValue(mod), nil } func getenv(key, fallback string) string { - value := os.Getenv(key) - if len(value) == 0 { - return fallback - } - return value + value := os.Getenv(key) + if len(value) == 0 { + return fallback + } + return value } func setVimMode(mode string) { @@ -194,7 +197,6 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.StringValue(cwd)), nil } - // read(prompt) -> input (string) // Read input from the user, using Hilbish's line editor/input reader. // This is a separate instance from the one Hilbish actually uses. @@ -212,7 +214,7 @@ func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // substitute with an empty string prompt = "" } - + lualr := &lineReader{ rl: readline.NewInstance(), } @@ -265,11 +267,13 @@ func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } switch typ { - case "left": - prompt = p - lr.SetPrompt(fmtPrompt(prompt)) - case "right": lr.SetRightPrompt(fmtPrompt(p)) - default: return nil, errors.New("expected prompt type to be right or left, got " + typ) + case "left": + prompt = p + lr.SetPrompt(fmtPrompt(prompt)) + case "right": + lr.SetRightPrompt(fmtPrompt(p)) + default: + return nil, errors.New("expected prompt type to be right or left, got " + typ) } return c.Next(), nil @@ -290,7 +294,7 @@ will look like: user ~ ∆ echo "hey --> ...!" -so then you get +so then you get user ~ ∆ echo "hey --> ...!" hey ...! @@ -386,7 +390,7 @@ func appendPath(dir string) { // if dir isnt already in $PATH, add it if !strings.Contains(pathenv, dir) { - os.Setenv("PATH", pathenv + string(os.PathListSeparator) + dir) + os.Setenv("PATH", pathenv+string(os.PathListSeparator)+dir) } } @@ -480,7 +484,7 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { interval := time.Duration(ms) * time.Millisecond timer := timers.create(timerTimeout, interval, cb) timer.start() - + return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil } @@ -571,7 +575,7 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // if dir isnt already in $PATH, add in if !strings.Contains(pathenv, dir) { - os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv) + os.Setenv("PATH", dir+string(os.PathListSeparator)+pathenv) } return c.Next(), nil @@ -625,14 +629,14 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { } switch mode { - case "emacs": - unsetVimMode() - lr.rl.InputMode = readline.Emacs - case "vim": - setVimMode("insert") - lr.rl.InputMode = readline.Vim - default: - return nil, errors.New("inputMode: expected vim or emacs, received " + mode) + case "emacs": + unsetVimMode() + lr.rl.InputMode = readline.Emacs + case "vim": + setVimMode("insert") + lr.rl.InputMode = readline.Vim + default: + return nil, errors.New("inputMode: expected vim or emacs, received " + mode) } return c.Next(), nil @@ -667,7 +671,9 @@ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #example // --This code will highlight all double quoted strings in green. // function hilbish.highlighter(line) -// return line:gsub('"%w+"', function(c) return lunacolors.green(c) end) +// +// return line:gsub('"%w+"', function(c) return lunacolors.green(c) end) +// // end // #example // #param line string diff --git a/exec.go b/exec.go index 4ed53a02..63dac576 100644 --- a/exec.go +++ b/exec.go @@ -29,12 +29,12 @@ func handleLua(input string) (string, uint8, error) { chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv())) 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) + /* 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 diff --git a/golibs/yarn/yarn.go b/golibs/yarn/yarn.go new file mode 100644 index 00000000..247566c9 --- /dev/null +++ b/golibs/yarn/yarn.go @@ -0,0 +1,130 @@ +package yarn + +import ( + "fmt" + "hilbish/util" + "os" + + "github.com/arnodel/golua/lib/packagelib" + rt "github.com/arnodel/golua/runtime" +) + +var yarnMetaKey = rt.StringValue("hshyarn") +var globalSpool *Yarn + +type Yarn struct { + initializer func(*rt.Runtime) + Loader packagelib.Loader +} + +type Thread struct { + rtm *rt.Runtime + f rt.Callable +} + +func New(init func(*rt.Runtime)) *Yarn { + yrn := &Yarn{ + initializer: init, + } + yrn.Loader = packagelib.Loader{ + Load: yrn.loaderFunc, + Name: "yarn", + } + + globalSpool = yrn + + return yrn +} + +func (y *Yarn) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { + yarnMeta := rt.NewTable() + yarnMeta.Set(rt.StringValue("__call"), rt.FunctionValue(rt.NewGoFunction(yarnrun, "__call", 1, true))) + rtm.SetRegistry(yarnMetaKey, rt.TableValue(yarnMeta)) + + exports := map[string]util.LuaExport{ + "thread": { + Function: yarncreate, + ArgNum: 1, + Variadic: false, + }, + } + + mod := rt.NewTable() + util.SetExports(rtm, mod, exports) + + return rt.TableValue(mod), nil +} + +func (y *Yarn) init(th *Thread) { + y.initializer(th.rtm) +} + +// create(fun) +// Creates a new, fresh Yarn thread. +func yarncreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + + fun, err := c.CallableArg(0) + if err != nil { + return nil, err + } + + yrn := &Thread{ + rtm: rt.New(os.Stdout), + f: fun, + } + globalSpool.init(yrn) + + return c.PushingNext(t.Runtime, rt.UserDataValue(yarnUserData(t.Runtime, yrn))), nil +} + +func yarnrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if err := c.Check1Arg(); err != nil { + return nil, err + } + + yrn, err := yarnArg(c, 0) + if err != nil { + return nil, err + } + + yrn.Run(c.Etc()) + + return c.Next(), nil +} + +func (y *Thread) Run(args []rt.Value) { + go func() { + term := rt.NewTerminationWith(y.rtm.MainThread().CurrentCont(), 0, true) + err := rt.Call(y.rtm.MainThread(), rt.FunctionValue(y.f), args, term) + if err != nil { + panic(err) + } + }() +} + +func yarnArg(c *rt.GoCont, arg int) (*Thread, error) { + j, ok := valueToYarn(c.Arg(arg)) + if !ok { + return nil, fmt.Errorf("#%d must be a yarn thread", arg+1) + } + + return j, nil +} + +func valueToYarn(val rt.Value) (*Thread, bool) { + u, ok := val.TryUserData() + if !ok { + return nil, false + } + + j, ok := u.Value().(*Thread) + return j, ok +} + +func yarnUserData(rtm *rt.Runtime, t *Thread) *rt.UserData { + yarnMeta := rtm.Registry(yarnMetaKey) + return rt.NewUserData(t, yarnMeta.AsTable()) +} diff --git a/lua.go b/lua.go index 90ad97fb..3a28f096 100644 --- a/lua.go +++ b/lua.go @@ -5,60 +5,31 @@ import ( "os" "path/filepath" - "hilbish/util" "hilbish/golibs/bait" "hilbish/golibs/commander" "hilbish/golibs/fs" "hilbish/golibs/snail" "hilbish/golibs/terminal" + "hilbish/golibs/yarn" + "hilbish/util" - rt "github.com/arnodel/golua/runtime" "github.com/arnodel/golua/lib" "github.com/arnodel/golua/lib/debuglib" + rt "github.com/arnodel/golua/runtime" ) var minimalconf = `hilbish.prompt '& '` func luaInit() { l = rt.New(os.Stdout) - l.PushContext(rt.RuntimeContextDef{ - MessageHandler: debuglib.Traceback, - }) - lib.LoadAll(l) - lib.LoadLibs(l, hilbishLoader) - // yes this is stupid, i know - util.DoString(l, "hilbish = require 'hilbish'") - - 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) - - // Add Ctrl-C handler - hooks.On("signal.sigint", func(...interface{}) rt.Value { - if !interactive { - os.Exit(0) - } - return rt.NilValue - }) - - lr.rl.RawInputCallback = func(r []rune) { - hooks.Emit("hilbish.rawInput", string(r)) - } - - lib.LoadLibs(l, fs.Loader) - lib.LoadLibs(l, terminal.Loader) - lib.LoadLibs(l, snail.Loader) - - cmds = commander.New(l) - lib.LoadLibs(l, cmds.Loader) + loadLibs(l) + yarnPool := yarn.New(yarnloadLibs) + lib.LoadLibs(l, yarnPool.Loader) // Add more paths that Lua can require from - _, err := util.DoString(l, "package.path = package.path .. " + requirePaths) + _, err := util.DoString(l, "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.") } @@ -74,6 +45,52 @@ func luaInit() { } } +func loadLibs(r *rt.Runtime) { + r.PushContext(rt.RuntimeContextDef{ + MessageHandler: debuglib.Traceback, + }) + lib.LoadAll(r) + + lib.LoadLibs(r, hilbishLoader) + // yes this is stupid, i know + util.DoString(r, "hilbish = require 'hilbish'") + + hooks = bait.New(r) + hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) { + fmt.Println("Error in `error` hook handler:", err) + hooks.Off(event, handler) + }) + lib.LoadLibs(r, hooks.Loader) + + // Add Ctrl-C handler + hooks.On("signal.sigint", func(...interface{}) rt.Value { + if !interactive { + os.Exit(0) + } + return rt.NilValue + }) + + lr.rl.RawInputCallback = func(rn []rune) { + hooks.Emit("hilbish.rawInput", string(rn)) + } + + lib.LoadLibs(r, fs.Loader) + lib.LoadLibs(r, terminal.Loader) + lib.LoadLibs(r, snail.Loader) + + cmds = commander.New(r) + lib.LoadLibs(r, cmds.Loader) +} + +func yarnloadLibs(r *rt.Runtime) { + r.PushContext(rt.RuntimeContextDef{ + MessageHandler: debuglib.Traceback, + }) + lib.LoadAll(r) + + lib.LoadLibs(r, hilbishLoader) +} + func runConfig(confpath string) { if !interactive { return