What is Mash?

Mash is the ``Make Shell.'' It is a simple scripting language used to build programs (and other files) from source files.

Language Overview

The Make Shell interprets a simple language. The language resembles that of the Bourne Shell and C Shell because, like those shells, the Make Shell is generally used to execute other programs.

Mash shell scripts are line-oriented. Each line is a statement. A statement can be a command to run, a definition of how to build a given target, a variable declaration, an ``if'' statement, a ``switch'' statement, a ``while'' loop, etc.

Here is a sample Mashfile.

Statements

Declarations

Declarations are used to set variable values. A variable name can contain alphanumerics and underscores. The only type of variable is the string array. (In the future, Mash may support numeric variables.) The format of a declaration is:
set variable = expression
The expression on the right-hand side of the equal-sign is evaluated as explained in the section on expressions.

A plus-equal sign in a declaration tells Mash to append the value of the expression to the end of the variable's current value. For example:

set CFLAGS = -O -g -Wall
set CFLAGS += -DPOSIX -DNO_STRDUP
will set CFLAGS to the five element list -O -g -Wall -DPOSIX -DNO_STRDUP.

Conditionals

Conditional statements are of the form:
if boolean expression
    statement
else
    statement
The boolean expression is evaluated as described in the section on expressions. The ``else'' clause is optional. There is no ``endif'' statement. It is not needed because the conditional statement clearly ends with the statement that follows the ``if'' or the ``else''.

As in C, a block of statements in curly braces always counts as one statement. Thus, you can conditionally execute multiple statements in the form:

if boolean expression {
    statement1
    statement2
} else {
    statement3
    statement4
}

Switch Statements

A switch statement has the following form:
switch expression
case tag
    statement
The switch statement can contain any number of cases. There is no endswitch statement. The switch statement is over when a line does not begin with the word "case" or the word "default". Each "case" line can contain multiple tags, separated by commas. If the switched expression matches any of the tags, then only the statement following that tag will be executed. (It does not fall through to the next case statement.) Optionally, there can be a line containing "default" and no tags. This represents a case that is always taken when reached, so do not put any more cases after the default case. The switched expression may be a multi-element list. For example, on a Linux/i386 system, the following code will output the number 2:
switch $SYS_NAME $SYS_ARCH
case Solaris SPARC, Irix MIPS
    echo 1
case Linux i386, OSF/1 Alpha
    echo 2
case HP/UX HP-PA, AIX PowerPC
    echo 3
default
    echo 4

Foreach Statements

A foreach statement has the following form:
foreach variable_name expression
    statement
For each string in the expression, it will set the given variable's value to that string and execute the statement. For example, the following code will output the numbers 1, 2, and 3 on separate lines:
foreach x (1 2 3)
    echo $x
The parentheses around (1 2 3) are optional. You may always put parentheses around any expression, and I feel that they make the statement more readable in this case.

While Statements

A while statement has the following form:
while boolean expression
    statement
The given statement will be executed repeatedly until the given boolean expression becomes false. There is no guarantee that this will ever happen.

Includes

An include statement has the form:
include file1 file2 file3...
Mash replaces the "include" command with the contents of each file it names. If the named file does not exist in the working directory, then Mash searches for it in each directory named by the MASH_INCLUDE_PATH variable. This variable is initialized to something meaningful on your system, so you should never replace it's value, just append to it. For example:
set MASH_INCLUDE_PATH += ./config/
include config.mash

Echo Statements

The command:
echo expression
outputs the value of the given expression.

Rules

A rule has the following form:
target target-expression: depends-expression
    statement
A rule tells Mash how to build targets. target-expression is evaluated as a list of targets that are built using the rule. depends-expression, which is optional, tells what files the targets that use this rule depend on.

The statement (or statement block) of a rule is not executed immediately. Initially, when mash reads the Mashfile, it evaluates the target-expression and depends-expression, and it stores the target's statement (unevaluated) in memory. After the entire Mashfile has been parsed, mash begins to build the targets specified on the command line. (If no targets were specified, mash starts to build the "all" target.)

To build a target, mash first attempts to recursively build every target named in the depends-expression of the target's rule. If any of these targets fails to build successfully, mash exits. If every target is built successfully, then mash checks for the existance of a file with the same name as the target. If that file does not exist, or if that file is older than some file listed in the depends-expression, then mash executes the target rule's statement.

A target is considered to have built successfully if the rule's statement executes successfully, or if the target file is found to be newer than all it's dependencies, or if there is no rule that specifies the target but a file with the name of the target exists. In the first case, where the statement executes successfully, it is not necessary for a file with the name of the target to be created.

In a rule's depends-expression, the variable $target is given special meaning. For example,

target foo bar: ${target}.o
says that foo depends on foo.o and bar depends on bar.o. It does not say that foo depends on bar.o or that bar depends on foo.o.

In a rule's statement (or statement block), the variable $target is used the same way, and the variable $depends is set to the entire depends-expression.

The statements in a rule can be Mash built-in commands or shell commands. That is, when the first word of a statement in a rule is not a built-in command, mash tries to find and run the named program.

There are other statements for defining rules: the "suffix" statement and the "depend" statement. I should document these.

Expressions

An expression is a list of ``tokens'' used to represent a list of strings. The tokens in the list are separated by whitespace. The amount of whitespace is irrelevant. The list ends at the end of the line. Each token in the list is evaluated as a string or a list of strings. The non-special characters are alpha-numerics, period, dash, slash, backslash, at-sign, underscore, plus, colon, and double-quote. All other characters are assumed to have special meaning in a token. (Some characters are not used yet but are reserved for future use.)

How tokens are evaluated:

Globbing: When a token contains a star or a question-mark, that token is evaluated as a (possibly-empty) list of files. The list of files is determined using the method of globbing detailed in the Posix.2 standard.

Variables A dollar-sign followed by a variable name is evaluated as the value of that variable. The name of the variable may optionally be enclosed in parentheses or curly braces. The value of the variable is substituted exactly. The value of the variable is not evaluated. For example, if the value of the variable contains a star, then the value of the expression will contain a star. The star will not be replaced by a list of files. If a token contains text before or after the variable, then that text is concatenated to the value. For example:

set variable1 = 1 2 3
set variable2 = prefix${variable1}suffix
will set variable2 to the three-element list prefix1 2 3suffix.

Functions: In a token, an ampersand followed by a function name followed by a list of arguments in parentheses is replaced by the value of that function call. Here is a simple example of a function call that will output the word "filename":

set variable = &basename(filename.c)
I plan to someday document the list of built-in functions supported by Mash.

Literal mode: When an apostrophe appears in a token, Mash enters literal mode. In this mode, all characters in the Mashfile go into the string until the closing apostrophe. This includes whitespace, newlines, non-printing characters, etc. There is no escape character in literal mode.

A literal string may be concatenated with a non-literal token. For example, the line:

set variable = '*'*
will set variable to the list of all files in the current working directory that begin with a star.

Semicolon: A semicolon ends the current token and the current list. It is exactly the same as a newline. For example:

set var1 = foo; set var2 = bar
Number sign: A number sign begins a comment. It ends the current token and the current list. All the text after a number sign until the end of line is ignored.

Parentheses: You may optionally put any expression in parentheses. Sometimes, it looks nice. For example, the following code sets two variables to the exact same three element list:

set var1 = a b c
set var2 = (a b c)
Within parentheses, newlines are treated as regular spaces.

Boolean Expressions

Whatever.