This page documents quake. Quake is a simple, specialized language and its interpreter drawing on elements of the C language, the Bourne shell, and the C pre-processor. The cm3 compiler includes a quake interpreter as its extension language. In fact, the configuration file, cm3.cfg, and m3makefiles are quake scripts.
Quake was designed to be a simple extension language for the builder. Building a complete, general-purpose language was not one of the goals.
Cm3 calls out to quake every time it needs to do something that needs to be specialized such as compiling C files or linking. Consult cm3.cfg in your installation (in the same directory as cm3 executable) for more information.
During the history of Modula-3, there have also been several different versions and implementations of quake. The first Modula-3 compilers didn't have any quake support at all. Unix makefiles were generated by the m3make utility from m3makefiles.
The first quake implementation was written in C and called as a standalone executable. Later, in the PM3 and CM3 distributions, quake was reimplemented in Modula-3, and then integrated into the builder (m3build) and later the compiler frontend, which led to considerable performance improvements of the Modula-3 compilation process.
This manual cannot achieve to document all the different variants and versions of quake. If not otherwise stated, it refers to the latest version of quake as it is contained in CM3 5.6.0 and later.
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.
Quake also has some limited support for integers and booleans (at least internally), those everything is converted on the fly to the needed type (which is almost always string). Denotations for boolean and integer values as well as arithmetic operations are mostly missing.
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. If builtin quake functions return boolean values, they use "TRUE" for true and "" for false.
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.
There is no explicit statement separator or terminator, which may seem quite unfamiliar to those used to C or Algol-derived languages. The next statement just starts after the previous is finished without semicolon or line-break.
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"
If local
or readonly
are not used, all
variables are located in the outermost (global) scope. Beware of
side-effects!
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 procedures 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
, error
, and exec
are variadic.
The list of built-ins was substantially extended in CM3 5.6 due to the needs of enhanced regression testing on different target platforms without further prerequisites.
Writes its arguments to the current output stream. Unlike the conversion of arrays to strings, there are no extra spaces inserted between arguments.
Writes its arguments to the standard error stream and stop running quake with an error return value.
The contents of file
is interpreted by quake
in place of the call to include
. This is analogous the
#include
directive in the C preprocessor. Calls to
include
nest until you run out of file descriptors or
something equally bad.
The arguments are catenated and passed to the operating system to be
executed as a child process. The command may start -
or
@
. These are stripped before the command is passed to the
command interpreter.
A prefix of @
indicates that the default action of
printing the command to the standard output stream before execution
should be overridden. A prefix character of -
overrides
quake's default action of aborting if it detects an error
return code after executing the command.
Same as the above, but imitating the old implementation which merged stdout and stderr streams.
Executes the quake code passed as a text.
The arguments are catenated and passed to the operating system to be
executed as a child process. The command may start -
or
@
. These are stripped before the command is passed to the
command interpreter.
A prefix of @
indicates that the default action of
printing the command to the standard output stream before execution
should be overridden. The exit code of the program is returned.
Same as the above, but imitating the old implementation which merged stdout and stderr streams.
Executes the passed command or command list which may contain the following input/output redirections:
< fn : read stdin from file fn > fn : write stdout into file fn 1> fn : write stdout into file fn 2> fn : write stderr into file fn &> fn : write stdout and stderr into file fn >> fn : append stdout to file fn 1>> fn : append stdout to file fn 2>> fn : append stderr to file fn &>> fn : append stdout and stderr to file fn
cmd
is parsed and split into single commands at every
;
, |
, &&
, and ||
.
The concatenation characters have the usual Bourne Shell meaning:
a ; b execute a followed by b a | b execute a and pipe its output to b's standard input a && b execute a, then b if a has succeeded (returned 0) a || b execute a, then b if a has failed (returned #0)
Token may be grouped by single or double quotes.
The exit code of the command list is returned.
Executes the given single command and pipe text
to its standard
input. No other redirections are performed.
The exit code of the command list is returned.
Executes the given single command and captures its stdout and stderr
output in text
This functions returns an array with two elements: First is the exit code of the command, second the captured output.
If the file src
differs from the file dest
,
or dest
is missing, copy src
to
dest
.
Touches file pn
, i.e. sets its access time to the current
time. If the files doesn't exist, it is created.
Removes file pn
if it exists.
Removes directory pn
if it exists and is empty.
Removes the complete directory hierarchy at pn
.
Creates all non-existing elements of pathname pn
as
directories.
Copies file pn
to pn2
.
Replaces the contents of file pn
with
text
. If the files does not exist, it is created.
target
is interpreted as a file name, as is
deps
(or the elements of deps
if it's an
array). If the files target
or deps
cannot
be found, or if (one of) deps
is more recent than target
, stale
returns true, otherwise false.
Deletes the file f
. Returns "TRUE" in case of success, else "".
Returns the directory of the currently executing file as a string.
Returns "TRUE" if pn
exists as a file system object, else
"" (false).
Returns "TRUE" if pn
is readable, else "" (false).
Returns "TRUE" if pn
is writable, else "" (false).
Returns "TRUE" if pn
is considered to be executable, else
"" (false).
Returns "TRUE" if pn
is a directory, else "" (false).
Returns "TRUE" if pn
is an ordinary file, else "" (false).
Returns an array of all subdirectories of pn
. If
baseonly
is "" (false), only the basenames are returned,
else pathnames are returned.
Returns an array of all ordinary files in directory pn
. If
baseonly
is "" (false), only the basenames are returned,
else pathnames are returned.
Returns the contents of file pn
as a text.
"TRUE" iff pn
is a valid pathname.
"TRUE" iff pn
is an absolute pathname.
Builds a new pathname from text[]
. The first element
is the root (which may be empty), the rest are arcs of the pathname.
Decomposes pn
into its root and arcs.
Strips the last arc from pn
.
Returns the last arc of pn
.
Strips the last extension from pn
.
Returns the last arc from pn
without its extension.
Returns the extension of the last arc of pn
.
Returns the concatenation of pna
and pnb
.
pnb
must be a relative pathname.
Returns the concatenation of pna
and pnb
and
the extension ext
.
pnb
must be a relative pathname.
Replaces the extension of the last arc of pn
.
Returns a representation of the special arc denotating the current directory.
Returns a representation of the special arc denotating the parent directory.
This function may be used to avoid the problems of limited space for
argument lists in some command interpreters. Some commands (notably
m3
, the Modula-3 driver program) are prepared to accept
arguments from a file. The syntax for this is -Ffile
.
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.
Returns true if foo
is defined, otherwise returns
false. Remember that the Boolean values false and true are represented
as empty and non-empty strings respectively. In this example,
foo
looks like a name, and is evaluated before begin
passed to defined
. So if you really want to find out
whether foo
is defined, use
defined("foo")
.
Returns true if the string, array, or table is empty, false otherwise.
Returns true if the strings foo
and bar
are
the equivalent. For arrays and tables, equal is treated as reference
equality and not as structural equality as in Modula-3.
Returns the string s
with backslash characters doubled.
Returns the string s
with each instance of
%s
replaced by successive a
arguments. The
number of a
arguments must match the number of
%s
s.
If the path p
is a prefix of the path q
,
returns the path from directory p
to file
q
. Otherwise, returns q
.
Splits the text into an array at every occurence of a character in
seps
.
Returns the subtext of text
starting at position
off
of length len
.
Removes all white space at the left of the text.
Removes all white space at the right of the text.
Removes all white space at the start and end of text.
Removes multiple empty lines within text
.
Returns the position of sub
within text
or
-1 if sub
is not found.
Returns true if sub
is contained in text
.
Returns "TRUE" for "true", "TRUE", "T", "yes", "YES", "1", otherwise "" (false).
Returns text
in which every character of a
is replaced with the character at the same position of b
.
a
and b
must be of the same length.
Returns text
in which every occurence of characters from
a
is removed.
Returns text
in which the subtext a
is
replaced by subtext b
for at most n
times.
Returns text
in which all environment variables with the
denotation ${name}
or $name
are replaced by
the values of the current process execution environment.
Returns an array of text
where each element is prefixed
with pre
.
Returns an array of text
where each element is suffixed
with suf
.
Pushes the current directory onto the stack and changes the working
directory of the quake process to dir
.
Pops the topmost directory from the directory stack and makes it the working directory of the quake process.
Changes the current directory of the quake process to dir
.
Returns the current working directory of the quake process.
Returns the hostname of the computer quake is running on.
Returns the current date in ISO format: YYYY-MM-DD.
Returns the current date and time in ISO format: YYYY-MM-DD hh:mm:ss.
Returns the current date and time in this format: YYYY-MM-DD-hh-mm-ss.
Sorry about the syntax for this. Suggestions (YACC permitting) welcome.
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.
Older versions of quake were used as standalone executables and run like this:
quake [definitions|files ...]
Each file argument is executed as it is encountered.
Since the integration of quake into the builder and compiler frontend, this is no more available (though it can be easily re-added it needed).
Quake is now run implicitly by invoking the compiler with
cm3 -build | -clean
Usually, the file src/m3makefile is executed. If
-override
or -x
is specified, the
definitions from src/m3overrides are executed before.
A definition has the form -Dvar
or
-Dvar=string
. The first form defines var
in
the global scope of the program, the second form gives it a value.
Original Author: Stephen Harrison.