msh
has a fixed number of built in data types
The source code for all these types is in a single file at: mshell/MShellObject.go.
Since brevity is a strong design goal, like most shells, many basic tokens can be written as-is. But unlike in most shells, we limit where the number of places literal tokens can be used. They can only be used in list literals, because that's how commands are input. Outside of lists, it will result in a error.
Bools are represented by
true
and
false
.
Quotations are blocks of code that can be executed. They are created by wrapping code in parentheses.
(1 2 +)
Lists are created by wrapping elements in square brackets. No commas are required between elements.
[1 2 3]
Lists can be added together with the +
operator. The result is a new list object.
[1 2] [3 4] + # [1 2 3 4]
msh
has double quotes and single quotes.
Double quoted strings have the following escape sequences:
\e
: Escape \n
: Newline \t
: Tab \r
: Carriage return \\
: Backslash \"
: Double quote If you have a backslash, and it isn't followed by one of the escape characters above, msh
will throw an error.
No escaping is done within single quoted strings or paths.
Since paths are such a common object to deal with in shell scripts, mshell has a dedicated type.
Paths are created by wrapping the path in backticks. No escaping is done.
If you need to escape or use string interpolation, then use double quotes and toPath
.
`path/to/file`
$"my/path/is/built/from/{variable}" toPath
Because paths are fundamentally different than strings, many functions change their behavior on whether the input is a path or a string.
For example, for a function like parseJson
, if the input is a path, it will read the file at that path and parse the contents as JSON.
Otherwise, it will parse the string as JSON directly.
The other place this is important is in command input redirection.
You often want to pipe input into a command, but sometimes it's from a file, and sometimes it's from a string.
In msh
, this choice can be made by using the appropriate type.
[wc -l] `myfile.txt` < ; # Counts the number of lines in myfile.txt
[wc -l] "line 1\nline 2\n" < ; # Counts the number of lines in the string "line 1\nline 2\n" (2)
mshell has the concept of dictionaries or associative arrays, but only with string keys (like awk
).
Dictionaries can be instantiated using dictionary literals, like:
{ "key": 1 }
Be careful with some of the lexing around the colon, as it's used with indexing. Best practice is a space between the colon and the value.
{ "key":1 } # Bad because ':1' is treated as index
Date/times can be entered using a literal syntax, in ISO-8601 format.
# All are valid
2023-10-01
2023-10-01T13
2023-10-01T13:01
2023-10-01T13:01:30
Dates can be subtracted from each other, and the result is a float number of days.
2023-10-02 2023-10-01 - # 1.0
mshell makes a distinction between integers and floats. Any literal number with a decimal will be lexed as a float, otherwise it will be an integer.
1 # This is a integer literal
1.0 # This is a float literal
The Maybe
type is a way to represent a value that may or may not exist.
It's like a better, strongly typed null value.
It's a value that can either be none
or a just[type]
value.
They can be instantiated using:
none # None value
"my string" just # Just value of a string
There are several functions with a Maybe type, but perhaps the most important is to just unwrap the value and fail entirely if it is none
.
This is done with the ?
operator.
"1" toFloat? # This will result in 1.0 being on the stack (Not wrapped)
"bad string" toFloat? # This will result in failure and execution will stop.
A pipe
is really a special list (and this type may get consumed by list
in the future).
It's created by the |
operator.
[
[ myCommand ]
[ wc -l ]
] | ; # Counting the number of lines of output from myCommand
Many times in shell programs you need to deal directly with binary data.
This type represents an array of bytes.
Example functions that takes binary data as input and output is utf8Str
and utf8Bytes
.