Language Guide: Syntax

Structure

The following examples all produce the same output but demonstrate the way the language is structured.

standard if / else block:

if { echo: foo\nbar -> match: bar } then {
    echo: "bar found"
} else {
    echo: "no bar found"
}

if evaluation pulled from stdin:

echo: foo\nbar -> match: bar -> if {
    echo: "bar found"
}

foreach line check value:

echo: foo\nbar -> foreach: line {
    if { echo: $line } then {
        echo: "bar found"
    } else {
        echo: "no bar found"
    }
}

However since the language is optimised for one liners, the idiomatic way of writing this code would be:

out: foo\nbar -> match: bar -> if { out: "bar found" }

The syntax is designed to be fast for writing logic while also readable. Which positions it between Bash and many other scripting languages (for example Perl and Python).

It's easier to write more complex logic than one would normally do in a shell scripting language (plus has a fewer hidden traps than traditional shells) and yet you still have the ability to quickly draft up Bash- style one liners in a way that many more advanced scripting languages normally struggle.

Processes

Processes are called by name then parameters. The process name can be separated by either a colon (:) and/or a white space character (, , , or ). eg out: "hello world".

Process names can be quoted (eg "out": "hello world") however this is not necessary unless you are calling an external executable with white space characters or a colon. Additionally characters can be escaped in the process name (eg: o\ut: "hello world").

Piping

To pass streams into processes you use pipes like you would in your standard shell. However murex pipes are typically written as arrows, ->, because that helps illustrate the direction of the stream and thus improves readability. However for compatibility reasons the standard pipe character is also supported

# Idiomatic murex:
out: "hello world" -> grep: "world"

# POSIX (Bourne, Bash, Zsh, etc) shells and also supported in murex:
out "hello world" | grep "world"

So while the idiomatic way to write murex code would be using arrow pipes, there isn't any danger in using the traditional pipe character since those two tokens are interchangeable.

Redirection

Another important difference in piping is the way redirection is handled. In murex you define redirection as the first parameter(s). For example:

err: <!out> "error message redirected to stdout"
out: <err> "output redirected to stderr"

The redirection must be the first parameter surrounded by less than / greater than tokens and can only be alpha and numeric characters. The prefixed bang (exclamation mark) denotes whether you are redirecting the stdout or stderr stream (stderr contains the !).

The advantage of this method is that you can create more meaningful named pipes for tunneling information between concurrent processes that might not otherwise sit on the same code pipeline.

pipe: foobar

# Create background process that outputs from the named pipe,
# then pipes that into a regexp matcher.
# <foobar> will stay running until it is closed.

bg {
    <foobar> -> regexp: m/00$/
    out: "pipe closed, exiting `bg`"
}

# Lets send some data to our named pipe, then close it.

a: <foobar> [1..1000]
!pipe: foobar

(PLEASE NOTE that murex named pipes are not file system FIFO objects)

null pipe

There is a builtin null device for forwarding output into a black hole. This null device is analogous to /dev/null on POSIX systems:

try <!null> {
    err "raise an error to fail `try`."
} -> catch {
    out "An error was raised but the message was dumped into `null`."
}

The null device doesn't need to be created.

File writer pipe

You can also use named pipes for writing files:

pipe: log --file error.log
try <!log> {
    err "Do something bad."
} -> catch {
    out "An error was raised. See error.log for details."
}

PLEASE NOTE that the parameter cannot be populated by variables. This is a security design to protect against a $variable containing <> and causing unexpected behaviour. Alternatively you can use as a function:

pipe: foobar
bg { <foobar> -> prefix "Hello, I am " }
out: "writing to foobar..." -> <foobar>
out: "closing foobar..." -> <foobar>
!pipe: foobar

Networking pipes

There are 4 networking pipes:

  1. tcp-dial: makes an outbound TCP connection
  2. udp-dial: makes an outbound UDP connection
  3. tcp-listen: listens for an incoming TCP connection request
  4. udp-listen: listens for an incoming UDP connection request

These are used in the same way as the other named-pipes described above.

pipe: google --tcp-dial google.com:80
out: <google> "GET /"
<google>
!pipe: google

Parameters

As you have probably guessed from the above examples, parameters are space delimited (much like with Bash) and support single and double quotations and escaping.

The quotes and escaping works in the same was as they do for function names:

No quotes

Parameters are space delimited.

out: a b c 1 2 3; translates to 6 parameters: "a", "b", "c", "1", "2", "3".

Single quotes and double quotes

This auto-escapes all characters expect the escape character (\) and variables (eg $variableName)

out: 'a b c' "1 2 3"; translates to 2 parameters: "a b c"s, "1 2 3".

Bang Prefix

Some builtins support an optional bang (!) prefix. These are similar to the bang operator in some languages where bang will perform a boolean not. However in murex, builtins prefixed with a bang will perform the opposite functional task rather than logical operation.

For example out: hello world -> !match: world would return no results as the !match builtin will look for strings that don't match "world" (rather than the typical operation of match looking for strings that do match). Some encoding / compression routines also support a bang prefix to decode or deflate their input.

End of chain semi-colons

Like on Linux/UNIX and cmd.exe, you can terminate a pipeline chain with a semi-colon (;).

To colon or not to colon?

It might seem weird to support a colon as a delimiter separating the function name and parameters but making it optional. The rational is that longer and more complex scripts look more readable with a colon since it clearly delimits what is a command from it's parameters. However this piece of punctuation remains optional to retain support for "Bash muscle memory".

The idiomatic style guide would be that any commands which have parameters that follow should contain a colon. eg:

out: "hello world" -> match: "world"

However if the parameter if a code block or JSON, then you drop the colon since the delimitation is already clear:

if { = 1==2 } then { out: "The laws of the universe are broken" }

Commands without a parameter don't need a colon and some of murex's primitive builtins ([, [[, (, =) also look cleaner without a colon.

However these are only recommendations for writing readable code rather than enforced standards.

Code golfing

Code "golfing" is the process of writing source code in the fewest number of characters possible (much like the sport of Golf). While code golfing isn't something sane people would advocate for normal programming practices (ie anything that requires any level of maintainability), sometimes when in the interactive command line you might wish to minify your code when typing a one-liner for the sake of convenience.

The syntax of murex is designed to be flexible enough to write readable and maintainable multi-line scripts but also to be terse enough to write "golfed" one-liners.

For example, one of the earlier code examples could be written like this:

out:foo\nbar|match:bar|if{(bar found)}

It is suggested that you don't write reusable code in this style and even the autocompletion tools built into murex's interactive command line will guide you towards writing readable code (albeit the autocompletion tools are built around intuitive and non-intrusive suggestions so are easy to ignore / override should you wish to)

This site is rebuilt weekly, the content is automatically generated from murex's source code. Last built on Mon Jun 29 06:14:55 UTC 2020 against commit 068e73e068e73e7ae60d2716f71eb252e3661dac57aecbf. Downloadable murex binaries are also built weekly. Current version is 0.50.1000 BETA which has been verified against tests.