Quake is a simple, specialized language drawing on elements of the C language, the Bourne shell, and the C pre-processor.
Quake was designed to be a component of m3build. Building a complete, general-purpose language was not one of the goals. M3build reads a set of quake language templates that define some operators, then executes an m3makefile in this context. The templates define procedures to build lists of modules, libraries, and so on.
Quake is designed to make it easy to assemble arrays and tables of strings. The value space is strings, arrays of strings, and tables of strings. An array is a list of elements, and a table is a set of key/value pairs where the keys in a table are all distinct.
A string is a sequence (delimited by `"') of characters excluding newline and double quote. Several special characters may be quoted (with `\') as follows:
new-line ignored \\ \ \n new-line \r return \t tab \b backspace \f formfeed \" double quote
An array is constructed using `[' and `]'. `[]' is the empty array, `["a"]' is an array of the single string "a". Elements are separated by `,'. Thus `["a", "b"]' is an array of two elements---the strings "a" and "b".
Arrays are flattened whenever they are referenced. This means that `["a", ["b", "c"]]' is converted the the array `["a", "b", "c"]'. This means that an array can never be the element of another array.
Arrays are accessed by an integer index. `a[2]' is the third element of the array a. The index expression may also be a string which will be converted to an integer. The range of the index is checked at run-time.
A table is constructed using `{' and `}'. `{}' is the empty table. Elements of a table are given as key/value pairs. An empty value may be omitted. `{"a", "b"}' is a table containing two keys---the strings "a" and "b". `{"p" : "q"}' is the constructor for a table with the single key "p" whose value is the string "q". Missing values are returned as "".
Tables are accessed using expressions of the form `s{"a"}'. This evaluates to the value of the key "a" in the table s.
Names in quake start with an letter (`a'..`z', `A'..`Z') or an underscore (`_'), followed by those characters, digits (`0'..`9'), hyphens (`-'), or periods (`.').
If the lookup for a name fails (it is undefined in any of the enclosing scopes) it is installed in the current scope with an initial string value of the text of the name.
C-style comments are supported (delimited by `/*' and `*/') and do not nest.
Single-line comments are introduced with `%' and terminated with a new-line.
A Boolean value is represented as a string. The empty string is false, and any other string is true.
An expression is:
string: "baz" name: foo environment variable: $CPU_TYPE array selector: array[5] array constructor: ["a", "b"] table selector: t{"a"} table constructor: {"a" : "p", b} function call: f(a, "b") parentheses: a and (b or f(c))
Operators are all left-associative, precedence is decreases from top to bottom in the following list.
& string catenation (`"Hello, " & foo') contains table inclusion (`s contains "a"') not logical negation (`not a') and logical conjunction (`c and not d') or logical disjunction (`a or b')
A note on string catenation. Operands of `&' are converted to strings whenever required and possible. Arrays and tables are converted to strings by catenating their elements (for tables, their keys) separated by a single spaces. For example, the expression
"a" & " " & ["b", "c"]
evaluates to the string "a b c".
A statement is either an assignment, a procedure definition, a procedure invocation, a conditional statement, or a loop.
Assign an expression (the string "bar") to the variable `foo' with
foo = "bar"
If `foo' already exists, and is an array, then
foo += "baz"
extends the array to include a new final element; the string "baz".
Quake has a global name space, but local scopes are always introduced when a procedure is called, and a `foreach' loop is executed.
Scopes are searched from innermost to outermost each time a name is looked up. The outermost scope is always the global scope.
Assignments can be made local to the innermost scope by prefixing the assignment statement with the keyword `local'. For example,
local foo = "bog"
In which case the values of any other instances of `foo' in other scopes are hidden. The previous value of `foo' is restored once the local scope is released by exiting the procedure or `foreach' loop.
To protect a name in the current scope, use
readonly dec = "digital"
Procedures may be defined in the global scope only. Here is the definition of a procedure `simple', taking two arguments bound to the local names `prefix' and `suffix' in the procedure's local scope.
proc simple(prefix, suffix) is q = prefix & "." & suffix end
The string `prefix & "." & suffix' is assigned to the global variable `q'.
This procedure can then be invoked with
simple("Hello", "m3")
Procedures can return values, in which case they become functions. For example,
proc combine(prefix, suffix) is return prefix & "." & suffix end
defines a function `combine' which catenates and returns the three strings `prefix', ".", and `suffix'. Now the function `combine' can be used in an expression, for example
q = combine("Hello", "m3")
assigns the string "Hello.m3" to `q'.
Values may be tested using the `if' statement. For example,
if compiling message = "compiling" end
If statements may have an else part, for example
if not (ready or interested) return else message = "congratulations" end
returns from the current procedure if the test succeeds, otherwise executes the assignment statement.
`Foreach' statements iterate over the string values in an array or in a table. For example,
foreach file in [".login", ".profile", ".Xdefaults"] write("Copying " & file & " to " & backup_dir & "0) copy_file(file, backup_dir) end
binds the name `file' to each of ".login", ".profile", and ".Xdefaults" in turn in a local scope. This example assumes that the procedures `copy_file', and the variable `backup_dir' have already been defined.
Here is a function `squeeze' to remove duplicates from an array
proc squeeze(array) is local t = {} foreach i in array t{i} = "" end return [t] end
Here is a list of reserved words in quake:
and contains else end foreach if in is local not or proc readonly return
Quake has a small collection of built-in procedures. Built-ins cannot be redefined. The built-ins `write' and `exec' are variadic.
Thus, `arglist("-F", really_huge_array)' returns either the original array if it's not really all that huge, or it creates a temporary file containing a list of the strings in the array (one to a line) and returns the string "-Ftemp" where `temp' is the name of the temporary file.
Output (from the `write' built-in procedure) may be temporarily redirected as follows:
> "foo.txt" in write("Hello, world0) end
The output of the calls to `write' is written to the file `foo.txt'.
Output may be appended to a file by using `>>' in place of `>'.
The special file names `_stdout' and `_stderr' are special and are bound to the file descriptors corresponding to normal and error terminal output respectively.
The default for output is the normal terminal stream.
Quake does not garbage collect. Reckless copying of large arrays can bloat the heap.
Stephen Harrison (sharrison@broadvision.com).