Comment by klibertp

11 hours ago

Ah, yeah, that makes sense, then.

I tried writing a shell in GNU Smalltalk, but had to mess with the parser - otherwise, the need to parenthesize subexpressions makes it really tedious. I implemented |> as a pseudo-operator that implicitly parenthesized everything before it, which was then removed from the expression. It worked for many simple cases, but wasn't pretty and quickly broke down for more complex ones.

I think the only other language worth investigating in this context is Raku. I avoided it for a long time due to its PERL ancestry, but it's actually a pretty well-designed language, and supports multimethods and overloading of pre-/post-/in- and circumfix operators. The operators support precedence and associativity. If you want an embedded DSL for shell one-liners, I think Raku could deliver. It unfortunately uses the comma for argument separation; you could maybe work around that with quotations and macros, but last I checked, these were experimental and not documented. There was a major effort to move to a better-designed parser that would support AST-based macros, but I'm not sure how far along that is today.

Io would work, though you'd also need to mess with a parser a bit (it's runtime-extensible, though, and also allows circumfix operators and passing unevaluated code as Message objects, which is very helpful in this case). Arguments are normally in parens and comma-separated, but you can simply ignore that. This: `some command --with arguments` is actually a valid Io expression (if `--` is a prefix operator), and since you're not evaluating the code, it doesn't matter if it doesn't make sense semantically. But Io is not very actively maintained (a shame!) It's still a very interesting design. It's described in "Seven Languages in Seven Weeks", if you want a quick intro.

Prolog would probably work, but you'd need to write a metainterpreter - otherwise, threading state in/out of predicates through logic variables would be a nightmare in the CLI. There's also the issue of commas as separators.

You know of TCL, so no need to mention it, though it is a pretty nice fit. A more exotic direction could be concatenative languages: maybe Factor, or Joy, or Kitten. All either experimental or unmaintained, currently. They tend to use whitespace for a separator, and I think they all allow for passing around unevaluated code one way or another.

Looking at the list - Red does look like one of the better candidates, but in normal Red code, all functions know their arity. This means you don't need separators for expressions. It doesn't work that way for shell commands, so you'd probably need to write a Parse grammar, include a separator to allow for variadic commands, and then, after splitting into subcommands, interpret the block command by command. I'm sorry, I'm not too knowledgeable about Red, so I can't give you more details, but that should be the general shape: `shell [ var: cmd1 arg --switch arg2 <some_separator> cmd2 <some_symbol>var ]` where `shell` passes the block to parse, gets a list of commands (including ones that bind results, which would appear as `var_name,command` pair; also, I think path expressions can be used in assignment, so `var/stderr: some command` could be included and handled easily), then walks through the list while setting vars and interpolating them in the following commands. Handling piping wouldn't be much harder than extending the Parse grammar and handling pipelines in the interpreting phase. Finally, you could add refinements on shell for simpler cases (just return exit code, capture all output, etc.) Subshells could be just nested blocks. Thinking about it, it could be a pretty fun way to learn more about Red. Ping me if you end up creating something like that, I'd be very interested to read :)

> btw, your website seems to be down.

I know - I broke my homegrown SSG some time ago and never had the time to fix it :( EDIT: restored some version of the site, 90% of the content still missing.