previous next top contents index framed top this page unframed
The features presented in this chapter give the programmer a way to perform arbitrarily complicated computations at compiletime. These computations can include calls to user-written PROCEDUREs which have been compiled as interfaces of other MODULEs, as well as to any of the MAINSAIL system PROCEDUREs.
19.1. $expr
$expr(x) compiles an expression x,
evaluates it at compiletime, then
replaces $expr(x) with a constant equal to x's
value. For example:
$expr($timeToStr($time))
is a STRING constant equal to the current time. $expr(x) may be used wherever an expression is allowed. Its type is x's type unless x is of type ADDRESS, CHARADR, or POINTER, in which case its type is LONG BITS and its value is cvlb(cva(x)) (this is not likely to be very useful).
For example:
MODULE usrMod (STRING PROCEDURE getNextLineToCompile);
...
REDEFINE nextLineToCompile = [] & $expr(getNextLineToCompile);
nextLineToCompile # compiler now compiles the body of the macro
getNextLineToCompile can be an arbitrarily complicated PROCEDURE that determines the text of the next line to be compiled. Concatenating with [] changes the result of getNextLineToCompile from a STRING constant to bracketed text, which can be compiled.
As another example, the following invokes sin at runtime since REAL arithmetic is not normally done at compiletime:
x := sin(1.5);
The following invokes sin at compiletime, thus assigning a constant to x:
x := $expr(sin(1.5));
The computation is done on the compiling machine, and
hence may not exactly agree with the runtime value.
19.2. $STMT
$STMT s; compiles the
statement s and then executes it at compiletime. The
terminating semicolon is required.
s can be a BEGIN statement, so that multiple statements
can be executed with a single $STMT.
$STMTB is an abbreviation for $STMT BEGIN:
$STMTB s1; ...; sn END;
For example:
$STMT write(logFile,eol & "The compiler is now here." & eol)
writes a message to logFile, like the MESSAGE compiler directive.
Declarations can be inserted immediately after $STMTB (or $STMT BEGIN):
$STMTB d1; ...; dn; s1; ...; sm END;
The declarations di and statements si are compiled, and then the si are executed at compiletime. The si can be omitted; i.e., $STMT can be used just for declarations. Identifiers declared using $STMT can be referenced within subsequent statements in the same $STMT, and within any subsequent $expr or $STMT (the declared identifiers are not local to a single $STMT). The identifiers are treated as if they had been declared in the outer block of a special-purpose, nameless intmod that is visible only from within $STMTs and $exprs. It is the programmer's responsibility to choose names for the identifiers declared in a $STMT that do not conflict with other identifiers (either in another $STMT, or in surrounding declarations or definitions).
None of the di can be a PROCEDURE declaration.
As a special case, an INTEGER or LONG INTEGER variable declared in a $STMT may be used as an iterative variable in the FOR-clause of an iterative statement in a $STMT (normally, iterative variables must be local variables).
As an example:
# define powerOf2Str to be "1, 2, 4, ..., <2^maxPowerOf2>"
$STMTB
INTEGER i; STRING s;
s := "";
FOR i := 0 UPTO maxPowerOf2 DOB
write(s,2L ^ i);
IF i < maxPowerOf2 THEN write(s,", ") END END;
DEFINE powerOf2Str = $expr(s);
$UNDCL i,s;
defines powerOf2Str to be the same thing as:
DEFINE powerOf2Str = "";
$FORC i = 0 UPTO maxPowerOf2 $DOC
REDEFINE powerOf2Str = powerOf2Str & cvs(2L ^ i);
IFC i < maxPowerOf2 THENC
REDEFINE powerOf2Str = powerOf2Str & ", ";
ENDC
ENDC
As another example:
$STMT ttyWrite(eol & "Module to compile: ");
DEFINE moduleToCompile = $expr(ttyRead);
is like:
DEFINE moduleToCompile "Module to compile: ";
except that the latter requires the user to put quotes around the reply
to make the compiler recognize it as a STRING constant.
19.3. Identifiers That May Be Accessed by $exprs and $STMTs
An $expr or a $STMT may reference macro identifiers,
identifiers declared inside $STMT as described
above, and interface fields of
existing MODULEs
(i.e., of MODULEs that have been declared at the
current point in the
compilation and for which an objmod exists so that the MODULE
can be allocated during compilation
by the compiler).
Specifically, it is possible to write a MODULE with an interface PROCEDURE, then compile the MODULE, then invoke the interface PROCEDURE at compiletime during a subsequent compilation using an $expr or a $STMT. The MODULE must be declared correctly within the MODULE that invokes it at compiletime so that the PROCEDURE is known at the point of use in the $expr or the $STMT.
During a cross-compilation (i.e., where the target system differs
from the host system),
system-dependent values within an $expr or $STMT
are those of the target rather than the
host. For example, if size(integerCode)
is used in an $expr, it will be the
value for the target system rather than for the host system. Such
expressions must be avoided in $exprs and $STMTs
if the host system value is really what is needed.
The two examples of Examples 19–1 and 19–2
solve this problem by using $STMT, $expr, and $UNDCL
to process
at compiletime a file where each line is to be used to initialize
a single ARRAY element.
The first example uses a $DOC
loop to get the constants from the file;
the second copies the input file to a temporary file,
converting its contents to acceptable MAINSAIL syntax,
and then sourcefiles the temporary file and then deletes it.
Example 19–1. Initialization Specifiers from a File
without the Use of an Auxiliary Temporary File
Example 19–2. Initialization Specifiers from a File
Using an Auxiliary Temporary File
19.4. $UNDCL
$UNDCL id1,...,idn; undeclares
identifiers id1 through idn, which must
have been declared earlier in the current MODULE within a $STMT.
No action occurs if an idi has not
been declared in a $STMT;
in particular, this situation is not an error.
$UNDCL
is necessary in order to avoid duplicate declarations if the
same $STMT is encountered several times (e.g., in a macro);
it is good
practice to undeclare each identifier declared in $STMT
after its last use.
19.5. A Sophisticated Example of $STMT, $expr, and
$UNDCL:
Using the Contents of a File as the Initialization Specifiers in
an INIT Statement
The initialization specifiers in an INIT
statement must be constants,
so they must be known at compiletime.
It is typically
more efficient to use an INIT statement than to read values from
a file to initialize an ARRAY element by element at runtime, but a
very long INIT statement in the middle of a program can reduce the
readability of the program.
PROCEDURE allocAry (PRODUCES STRING ARRAY(1 TO *) ary);
BEGIN
# allocate and initialize ary so that its ith element is
# the ith line of a file
# count number of lines in the file -- THIS IS DONE AT
# COMPILETIME
$STMTB
INTEGER numVals; POINTER(textFile) f; STRING s;
open(f,eol & "File with names: ",prompt!input);
numVals := 0;
DOB read(f,s); IF NOT s THEN DONE; numVals .+ 1 END;
s := f.name; close(f) END;
new(ary,1,$expr(numVals));
INIT ary (
# read each line and make it next element of array
$STMT open(f,s,input); DEFINE firstVal = TRUE;
$DOC
$STMT read(f,s);
IFC NOT $expr(s) THENC $DONEC; ENDC
IFC firstVal THENC REDEFINE firstVal = FALSE;
ELSEC , ENDC
$expr(s) ENDC
$STMT close(f);
);
$UNDCL numVals,f,s;
END;
PROCEDURE allocAry (PRODUCES STRING ARRAY(1 TO *) ary);
BEGIN
# allocate and initialize ary so that its ith element is
# the ith line of a file
# put the source text for the init values in a file
$STMTB
INTEGER numVals; POINTER(textFile) f,g; STRING s;
$createUniqueFile(g,create!output);
open(f,eol & "File with names: ",prompt!input);
numVals := 0;
DOB read(f,s); IF NOT s THEN DONE;
IF numVals THEN cWrite(g,',');
write(g,eol,"""",s,"""");
# assumes no quotes in s
numVals .+ 1 END;
close(f); s := g.name; close(g) END;
new(ary,1,$expr(numVals));
INIT ary (SOURCEFILE $expr(s););
$STMT $delete($expr(s)); # delete the temp file
$UNDCL numVals,f,g,s; # undeclare the identifiers
END;