Quake(7) MODULA-3 PROGRAMMERS MANUAL Quake(7)
NAME
quake- The Quake extension language and interpreter
SYNOPSIS
quake [options]
DESCRIPTION
Quake is a simple, specialized language and its interpreter draws on
elements of the C language, the Bourne shell, and the C pre-processor.
The cm3 compiler includes a quake interpreter as its extension lan-
guage. In fact, the configuration files, cm3.cfg, m3makefiles, and
m3overrides are quake scripts.
Quake was designed to be a simple extension language for the Modula-3
builder. Building a complete, general-purpose language was not one of
the goals. The functionality of the builder (i.e., the quake engine)
has been incorporated within the cm3 compiler itself, and is no longer
a separate executable.
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.
HISTORY
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 stan-
dalone 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.
LANGUAGE
VALUES
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.
STRINGS
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
ARRAYS
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.
TABLES
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 omit-
ted. {"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 the
empty string "".
Tables are accessed using expressions of the form s{"a"}. This evalu-
ates to the value of the key "a" in the table s.
NAMES
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.
COMMENTS
C-style comments are supported (delimited by /* and */) and do not
nest. Single-line comments are introduced with % and terminated with a
new-line.
CONDITIONALS
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.
EXPRESSIONS
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".
STATEMENTS
A statement is either an assignment, a procedure definition, a proce-
dure 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 semi-
colon 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".
SCOPE
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
Procedures may be defined in the global scope only. Here is the defi-
nition 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.
CONDITIONAL STATEMENTS
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 exe-
cutes the assignment statement.
LOOPS
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
KEYWORDS
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.
Input / Output
write(...)
Writes its arguments to the current output stream. Unlike the
conversion of arrays to strings, there are no extra spaces
inserted between arguments.
error(...)
Writes its arguments to the standard error stream and stop run-
ning quake with an error return value.
Code Execution
include(file)
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.
exec(...)
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 exe-
cuting the command.
cm3_exec(...) compatibility, since CM3 d5.5.1
Same as the above, but imitating the old implementation which
merged stdout and stderr streams.
quake( code ) since CM3 d5.6.0
Executes the quake code passed as a text.
try_exec(...) --> int
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.
try_cm3_exec(...) --> int compatibility, since CM3 d5.5.1
Same as the above, but imitating the old implementation which
merged stdout and stderr streams.
q_exec( cmd ) --> int since CM3 d5.6.0
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.
q_exec_put( cmd, text ) --> int since CM3 d5.6.0
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.
q_exec_get( cmd ) --> [int, text] since CM3 d5.6.0
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.
File System Functions and Procedures
cp_if(src,dest)
If the file src differs from the file dest, or dest is missing,
copy src to dest.
fs_touch( pn ) since CM3 d5.6.0
Touches file pn, i.e. sets its access time to the current time.
If the files doesn't exist, it is created.
fs_rmfile( pn ) since CM3 d5.6.0
Removes file pn if it exists.
fs_rmdir( pn ) since CM3 d5.6.0
Removes directory pn if it exists and is empty.
fs_rmrec( pn ) since CM3 d5.6.0
Removes the complete directory hierarchy at pn.
fs_mkdir( pn ) since CM3 d5.6.0
Creates all non-existing elements of pathname pn as directories.
fs_cp( pn, pn2 ) since CM3 d5.6.0
Copies file pn to pn2.
fs_putfile( pn, text ) since CM3 d5.6.0
Replaces the contents of file pn with text. If the files does
not exist, it is created.
stale(target, deps) --> bool
target is interpreted as a file name, as is deps (or the ele-
ments 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.
unlink_file(f) --> bool
Deletes the file f. Returns "TRUE" in case of success, else "".
path() --> text
Returns the directory of the currently executing file as a
string.
fs_exists( pn ) --> bool since CM3 d5.6.0
Returns "TRUE" if pn exists as a file system object, else ""
(false).
fs_readable( pn ) --> bool since CM3 d5.6.0
Returns "TRUE" if pn is readable, else "" (false).
fs_writable( pn ) --> bool since CM3 d5.6.0
Returns "TRUE" if pn is writable, else "" (false).
fs_executable( pn ) --> bool since CM3 d5.6.0
Returns "TRUE" if pn is considered to be executable, else ""
(false).
fs_isdir( pn ) --> bool since CM3 d5.6.0
Returns "TRUE" if pn is a directory, else "" (false).
fs_isfile( pn ) --> bool since CM3 d5.6.0
Returns "TRUE" if pn is an ordinary file, else "" (false).
fs_lsdirs( pn, baseonly ) --> text[] since CM3 d5.6.0
Returns an array of all subdirectories of pn. If baseonly is ""
(false), only the basenames are returned, else pathnames are
returned.
fs_lsfiles( pn, baseonly ) --> text[] since CM3 d5.6.0
Returns an array of all ordinary files in directory pn. If
baseonly is "" (false), only the basenames are returned, else
pathnames are returned.
fs_contents( pn ) --> text since CM3 d5.6.0
Returns the contents of file pn as a text.
Pathname Functions
pn_valid(pn) --> bool
"TRUE" iff pn is a valid pathname.
pn_absolute(pn) --> bool
"TRUE" iff pn is an absolute pathname.
pn_compose(text[]) --> text
Builds a new pathname from text[]. The first element is the root
(which may be empty), the rest are arcs of the pathname.
pn_decompose(pn) --> text[]
Decomposes pn into its root and arcs.
pn_prefix(pn) --> text
Strips the last arc from pn.
pn_last(pn) --> text
Returns the last arc of pn.
pn_base(pn) --> text
Strips the last extension from pn.
pn_lastbase(pn) --> text
Returns the last arc from pn without its extension.
pn_lastext(pn) --> text
Returns the extension of the last arc of pn.
pn_join(pna, pnb) --> text
Returns the concatenation of pna and pnb. pnb must be a relative
pathname.
pn_join2(pna, pnb, ext) --> text
Returns the concatenation of pna and pnb and the extension ext.
pnb must be a relative pathname.
pn_replace_text(pn) --> text
Replaces the extension of the last arc of pn.
pn_current() --> text
Returns a representation of the special arc denotating the cur-
rent directory.
pn_parent() --> text
Returns a representation of the special arc denotating the par-
ent directory.
Argument Lists
arglist(pfx, array)
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.
Generic Predicates
defined(foo) --> bool
Returns true if foo is defined, otherwise returns false. Remem-
ber 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").
empty(foo) --> bool
Returns true if the string, array, or table is empty, false oth-
erwise.
equal(foo, bar) --> bool
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.
Text Functions
escape(s) --> text
Returns the string s with backslash characters doubled.
format(s, a...) --> text
Returns the string s with each instance of %s replaced by suc-
cessive a arguments. The number of a arguments must match the
number of %ss.
normalize(p, q) --> text
If the path p is a prefix of the path q, returns the path from
directory p to file q. Otherwise, returns q.
split( text, seps ) --> text[] since CM3 d5.6.0
Splits the text into an array at every occurence of a character
in seps.
sub( text, off, len ) --> text since CM3 d5.6.0
Returns the subtext of text starting at position off of length
len.
skipl( text ) --> text since CM3 d5.6.0
Removes all white space at the left of the text.
skipr( text ) --> text since CM3 d5.6.0
Removes all white space at the right of the text.
BR "compress( text ) --> text" " since CM3 d5.6.0"
Removes all white space at the start and end of text.
squeeze( text ) --> text since CM3 d5.6.0
Removes multiple empty lines within text.
pos( text, sub ) --> int since CM3 d5.6.0
Returns the position of sub within text or -1 if sub is not
found.
tcontains( text, sub ) --> bool since CM3 d5.6.0
Returns true if sub is contained in text.
bool( text ) --> bool since CM3 d5.6.0
Returns "TRUE" for "true", "TRUE", "T", "yes", "YES", "1", oth-
erwise "" (false).
subst_chars( text, a, b ) --> text since CM3 d5.6.0
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.
del_chars( text, a ) --> text since CM3 d5.6.0
Returns text in which every occurence of characters from a is
removed.
subst( text, a, b, n ) --> text since CM3 d5.6.0
Returns text in which the subtext a is replaced by subtext b for
at most n times.
subst_env( text ) --> text since CM3 d5.6.0
Returns text in which all environment variables with the denota-
tion
${name} or $name are replaced by the values of the current pro-
cess execution environment.
add_prefix( text[], pre ) --> text[] since CM3 d5.6.0
Returns an array of text where each element is prefixed with
pre.
add_suffix( text[], suf ) --> text[] since CM3 d5.6.0
Returns an array of text where each element is suffixed with
suf.
Directory (Stack) Functions and Procedures
pushd( dir ) since CM3 d5.6.0
Pushes the current directory onto the stack and changes the
working directory of the quake process to dir.
popd() since CM3 d5.6.0
Pops the topmost directory from the directory stack and makes it
the working directory of the quake process.
cd( dir ) since CM3 d5.6.0
Changes the current directory of the quake process to dir.
getwd() --> text since CM3 d5.6.0
Returns the current working directory of the quake process.
System Information
hostname() --> text since CM3 d5.6.0
Returns the hostname of the computer quake is running on.
date() --> text since CM3 d5.6.0
Returns the current date in ISO format: YYYY-MM-DD.
datetime() --> text since CM3 d5.6.0
Returns the current date and time in ISO format: YYYY-MM-DD
hh:mm:ss.
datestamp() --> text since CM3 d5.6.0
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 redi-
rected 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.
The quake interpreter is built into the CM3 Modula-3 compiler, whereas
it was a separate executable for earlier ones, such as PM3.
The Critical Mass Modula-3 compiler cm3(1), and the man pages for
m3makefile(7), and m3override(7) files.
AUTHOR
(man page) Peter Eiserloh (eiserlohpp -at- yahoo.com)
(original author) Stephen Harrison.