rash is a minimal shell which supports asynchronous operations by saving its program state to a file, then terminating itself, and later being started again by the user, where it will read and continue from the saved program state.
Example programs are located in the examples subdirectory.
You can find more programs here: https://github.com/search?q=repo%3Aathas%2FEggsML+%22%23%21%2Fusr%2Fbin%2Fenv+rash%22&type=code
Run cabal install to build the rash executable and place it in
~/.local/bin. This requires the cabal tool to be present on your
system. Please see
https://cabal.readthedocs.io/en/latest/getting-started.html for how to
install this.
Alternatively, you can install Nix and then either
- run
nix-shellto get into a sub-shell with the required development dependencies present, and then runcabal installthere, or - run
nix-buildto build the program straight away, which will put a binary intoresult/binthat you can then run directly or manually copy to~/.local/binor another directory of your choosing, or - run
nix-build static.nixto build a statically linked program.
Run cabal test to run unit tests.
rash is a state-saving pseudo-shell tailored for IRC bots, but
hopefully also applicable in other cases.
When its read instruction is encountered, the program exits. When the
program is run again, it restarts at that read instruction and uses
the command-line arguments as the read input instead of using standard
in. This continues until there are no more instructions to run.
rash is an assembly-like language. Each line is an instruction. It
has the following types of instructions:
# Some text.- A comment.
Instructionconstructor: None, optimized away
a_var=some text- Assignment of text "some text" to variable
a_var. Instructionconstructor:Assign
- Assignment of text "some text" to variable
>command arg0 arg1 ... argN- Run
command arg0 arg1 ... argNin a shell, wait for it to finish, and print its output. Instructionconstructor:Run
- Run
read x- Exit. When restarted, read the command line arguments into the
variable
x. Instructionconstructor:Read
- Exit. When restarted, read the command line arguments into the
variable
:your_label- A label for jumping.
Instructionconstructor: None, optimized away
j your_label- Jump unconditionally to the
your_labellabel. Instructionconstructor:Jump
- Jump unconditionally to the
jz somewhere- Jump to the
somewherelabel if the previous command exited with return code 0. If no command has previously run, the return code is 0. Instructionconstructor:JumpIfRetZero
- Jump to the
exit- Remove all state and exit. This also happens if the end of the program is reached.
Instructionconstructor:Exit
tribbles=>ls stuff- Run
ls stuffin a shell, wait for it to finish, and redirect its standard out into thetribblesvariable. Instructionconstructor:AssignRun
- Run
<some text>grep -vi bar- Run
grep -vi barwith "some text" redirected into standard in, and print its output. This can be used to simulate pipes. Instructionconstructor:Run
- Run
foo=<some text>grep -vi bar- Run
grep -vi barwith "some text" redirected into standard in, and redirect its standard out into thefoovariable. Instructionconstructor:AssignRun
- Run
To use a variable in text fields, write ${variable}. This can be used
in commands, assignment values, and command inputs, and it can be mixed
with just text. For example, this:
read url_base
>grep http://${url_base}/robots.txt crawls
first reads command line arguments into the url_base variable, and
then runs grep with, among other text, the url_base value as the
first argument.
Whitespace and lack thereof is significant, because why not?
For now, no special characters can be escaped.
You can use the special variable ${initial_arguments} to get the
initial arguments passed to your program.
Rash saves its state to files in a directory. The directory is determined by this algorithm:
- Is
$RASH_STATE_DIRset? Then save the state in$RASH_STATE_DIR. If not, go to step 2. - Is
$XDG_HOME_STATEset? Then save the state in$XDG_HOME_STATE/rash, as per the XDG Base Directory Specification. If not, go to step 3. - Save the state in
$HOME/.local/state/rash.
If the given directory does not exist, rash will create it.
The pipeline of rash loading a new file looks like this:
-
Parse the file into a list of intermediate
Rash.Representation.Parse.Instructioninstructions. -
Convert the
Rash.Representation.Parse.Assemblyinto an optimizedRash.Representation.Internal.Assemblytype, where jumps to labels are converted to jumps to absolute instructions, and variable names are converted to variable indexes. Also turn all strings intoData.Text.Textvalues. -
Interpret.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
rash was originally developed within the EggsML monorepo as part of the toolset of an IRC bot, but has now been set loose upon the rest of the world.