#shell interpreter builtins
1 messages · Page 1 of 1 (latest)
this is currently not possible; builtins kick in before the exec handler:
if isBuiltin(name) {
r.exit = r.builtinCode(ctx, pos, name, args[1:])
return
}
r.exec(ctx, args)
have you looked into the call handler? https://pkg.go.dev/mvdan.cc/sh/v3/interp#CallHandler
it might be enough for you but it depends on what you mean by "add some convenience".
I was wondering if it was possible to add support for it. Including making isBuiltin() public since right now we’ve duplicated it on our side, but not maintainable.
have you looked into the call handler? https://pkg.go.dev/mvdan.cc/sh/v3/interp#CallHandler
Yeah, and we use it, but it’s only as an alias so we can expose some builtins and hide others, along with a help page (e.g., .help .wait). Most of our CallHandler is working around interpreter builtins running first. For context, basically we hide interpreter builtins behind a _ prefix in order to allow having a Dagger function named “echo” for example. It also allows us to only expose a subset through our own builtins prefix (.).
What I’m after here though is a way to call builtins from within an exec handler. I don’t mean that’s the only thing the exec handler does, I mean an exec handler that does other things and at some point may relay some logic to an interpreter builtin.
That’s just a more recent example where I found the need for it. echo is easy to replicate, but not wait because it needs access to some internals. Depending on the use case there’s some added boilerplate necessary to properly handle failures. We could wrap wait in our own .wait, for example, in order to help reduce some of that boilerplate from the shell.
gotcha. I think exposing the list of builtins, and exposing the builtins themselves, is reasonable - I just wanted to check you were aware of CallHandler first.
I'll need to think about what the right API is here, because I'm not sure that simply exposing them as an ExecHandler API is the right thing to do.
I don't think the ExecHandler API will work for builtins, because many of them are closely tied to the internal Runner state, such as cd, set, alias, etc.
So it will have to be something a bit bespoke. More tomorrow.
I've exported interp.IsBuiltin now, FYI. I'm still working through an API to expose the builtins themselves.
Cool! So you've figured out the design for exposing it and going through implementation or figuring out both as you go?
trial and error, currently.
after a bit more refactoring, and three failed attempts, here's where I landed: https://github.com/mvdan/sh/commit/6b98b83b673469da62330f807fd989eb434acb96
what do you think? see the added tests.
I initially added Runner.Builtin, but quickly realised it sounded like it could be called from anywhere, which is not the intent. I think anyone wanting to run a builtin from the top level API should do it the usual way, via Runner.Run.
hanging it off of HandlerContext is a bit weird but I think it works. I added a hidden Runner field there so that you don't have to keep and pass the runner pointer yourself.
I did not push to master because I wanted to run it past you before people it lands. It wouldn't be stable at master yet, but I also want to avoid experimenting in master too much.
Works for me! 🙂
👍 I will polish this up and push to master this evening
"this evening' ended up being the next week, but here: https://github.com/mvdan/sh/commit/4e01dd2d8a0818c9ed4b10de5daa7070246a9a57
I have some time this weekend, so let me know if you need any other adjustments or investigation
Seems to be working well: https://github.com/dagger/dagger/pull/10438