previous next top contents index framed top this page unframed
15.1. DEFINE
A macro equate associates an identifier (the macro name or
the macro) with text or a constant expression
(the macro body) that is
substituted by the compiler for subsequent occurrences of the
identifier (macro calls).
A macro definition list consists of the keyword DEFINE (or the
keyword REDEFINE; see Section 15.2) followed by
a series of one or more macro equates, as follows:
DEFINE macroEquate1,macroEquate2,...,macroEquateN;
The form of a simple macro equate is:
v = macroBody
where v is an identifier and macroBody is a constant or “bracketed text” (Section 15.3).
If macroBody is a constant, the identifier defined is called a macro constant. macroBody may be a constant expression of any data type. For example:
DEFINE maxNum = 10;
defines the identifier maxNum to be 10. During compilation, any subsequent occurrences of maxNum in the MODULE are replaced with the constant 10 (as if the number 10 appeared instead of maxNum).
The form of a macro equate with parameters is:
v(v1, ..., vn) = [bracketed text]
where the macro identifier v is followed by a parenthesized list of parameter identifiers (the vi) that may be used within the bracketed text (vn may optionally be preceded by the keyword REPEATABLE; see Section 15.6.1). Subsequent occurrences of v (i.e., macro calls) are followed by a parenthesized list of arguments, much like a PROCEDURE call. Each occurrence of the identifier vi within the bracketed text (even within STRING constants and comments) is replaced with the corresponding argument text. Macro arguments are described in Section 15.5.
A form of macro equate that involves compiletime interaction with the programmer is described in Section 15.4.
A macro definition list may occur almost anywhere in a program, even in the midst of an expression, for example. A macro definition list cannot occur in the midst of another definition list, except within bracketed text.
Macro identifiers may be used anywhere, even in subsequent macro definition lists. For example, if upperLimit is defined as:
DEFINE upperLimit = 100;
then a subsequent macro definition:
DEFINE threeTimesUpperLimit = 3 * upperLimit;
is equivalent to:
DEFINE threeTimesUpperLimit = 3 * 100;
A macro definition list within a PROCEDURE body defines new macros that are accessible only within the body of the PROCEDURE. After the end of the PROCEDURE body, any earlier definitions (or declarations) of the macro identifiers are again in effect.
In a macro definition headed by DEFINE, the compiler issues an error message if any of the identifiers defined has been previously defined. This restriction is not applied to macro definitions headed by REDEFINE. Macro identifiers in macro definition lists headed by REDEFINE are given new definitions whether the identifiers were previously defined or not.
A REDEFINE within a PROCEDURE body may change the body of a macro defined outside the PROCEDURE body. The new macro body remains in effect throughout the rest of the MODULE (or until a new REDEFINE of the macro is encountered).
REDEFINE may be used to increment a counter as shown in Example 15–1. The macro x is defined to be 0 originally. Whenever a call to the macro def occurs, x is redefined to have a value one greater than its previous value, and the argument to def is defined to have this new value of x. Thus the macro calls:
def(case1)
def(case2)
def(case3)
result in casei being defined as
i. $def is a more sophisticated,
predefined version of def; see Section 16.24.
15.3. Bracketed Text
Bracketed text is a sequence of characters enclosed in matching
brackets ([ and ]).
It is used in a macro body to define a macro
as almost arbitrary text. The characters are taken just
as is when building the bracketed text; e.g., macro calls are not
expanded and compiler directives are ignored.
As an example of bracketed text, the macro definition:
DEFINE verOk = [testSkill(2 * skNum,5,15)];
allows a programmer to use verOk to stand for:
testSkill(2 * skNum,5,15)
throughout the scope of the definition.
Brackets may appear within the text if they are matched; i.e., each left bracket must be followed by a matching right bracket, and each right bracket must be preceded by a matching left bracket.
A macro constant definition such as:
DEFINE bound = 100;
could be written with the same effect using bracketed text as:
DEFINE bound = [100];
but the former is more efficiently compiled.
15.4. Interactive Definition
A macro equate may omit the = and subsequent macro body, in
which case the compiler prompts for and reads a line from cmdFile
and uses this line to define the body of the macro.
For example:
DEFINE v1, ..., vn;
causes the compiler to write to logFile for each identifier vi:
DEFINE Vi =
(where Vi is vi converted to upper case). It then reads a line from cmdFile. The text:
DEFINE Vi = line read from cmdFile;
is then compiled as if it had appeared in the source file.
Another option is to supply a STRING constant expression that is written to logFile in place of DEFINE Vi =. An example is:
DEFINE v1 c1, ..., vn cn;
where the ci are STRING constant expressions. In this case, for each vi, the compiler writes ci instead of the standard DEFINE Vi = message.
For example, when the compiler encounters:
DEFINE maxNumInput,debug "debugging version (TRUE or FALSE)? ";
it first types:
DEFINE MAXNUMINPUT =
If the user types 10, for example, the effect is the same as if:
DEFINE maxNumInput = 10;
had occurred in the program.
The compiler then types:
debugging version (TRUE or FALSE)?
to which the user replies either TRUE or FALSE. If TRUE is typed, for example, the effect is the same as if:
DEFINE debug = TRUE;
had occurred in the program. Thus, this form of macro equate allows the programmer to interact with the compilation.
Any mixture of the various forms of macro equate can occur with the same macro definition list, as in:
REDEFINE
debug = TRUE,
callFoo(i) = [foo(i,1)],
version,
compileAllModules
"Compile all modules (TRUE or FALSE): ",
upperBound = 10;
It is possible to have an interactive define of a macro header that contains parameters, e.g.:
DEFINE xxx(yyy) "xxx(yyy): ";
When the compiler prompts for the definition of this macro, the user's
response must be bracketed text.
15.5. Macro Calls
A macro call is the occurrence of a macro identifier at any
point in a program after it has been defined. It directs the compiler
to scan the body of the macro as if it appeared in place of the macro
call.
If the macro was defined with parameters (see Section 15.1), a parenthesized list of macro arguments (see Section 15.6) separated with commas may appear after the macro identifier. Fewer arguments may be supplied than parameters, in which case the compiler supplies no text (i.e., acts as if an empty pair of brackets were supplied) for each unspecified argument. No parentheses are needed if no arguments are specified.
The macro arguments replace all occurrences of the corresponding
parameter identifiers in the macro body, as in Example 15–1.
15.6. Macro Arguments
Most macro arguments may consist of the intended text with
no special delimiters. But if the macro argument is a
text fragment
(e.g., if it contains unmatched parentheses), then it must
be enclosed in brackets. An argument with unmatched brackets is not
allowed.
The text of each macro argument starts with the first character (other than the whitespace characters space, tab, eol, or eop) following the previous terminating comma (or the opening left parenthesis of the argument list).
If the first character of a macro argument is not a left bracket, then the text of the argument is terminated with the next comma (or the closing right parenthesis) except that nesting counts are kept of parentheses and brackets; the argument text does not terminate until each nesting count is zero. That is, each time a left parenthesis (bracket) is encountered, the parenthesis (bracket) nesting count is incremented by one and each time a right parenthesis (bracket) is encountered, the appropriate count is decremented by one. The macro argument scan does not terminate until both counts are zero and a comma or right parenthesis is encountered. Trailing characters such as space, tab, eol, and eop are removed from the argument text. Comments are discarded; i.e., if # is encountered, the remainder of the line is removed from the argument text.
STRING constants are treated as a unit; i.e., when a double quote is found, the compiler immediately scans for the end of the STRING constant (as described in Section 3.6). Parentheses, commas, or brackets that occur in the STRING constant are not specially processed.
Commas may appear within properly nested parentheses, brackets, or STRING constants.
If the first character of a macro argument is a left bracket ([), then the argument is the sequence of characters up to the next matching right bracket (a nesting count, as described above, is kept for brackets, and the argument text terminates when the count is zero). This allows almost arbitrary text to be used as a macro argument; i.e., no attention is paid to parentheses, commas, STRING quotes, or comments within square brackets.
15.6.1. REPEATABLE Macro Parameters, $numArgs, $arg, and
$sArg
The last parameter of a macro may be declared REPEATABLE:
DEFINE foo(a,b,REPEATABLE v) = [...];
In a call to such a macro, the arguments that correspond to the REPEATABLE parameter are treated as if they had been enclosed in square brackets, i.e., as if they were a single argument; given foo defined as above, foo(i,j,k,l,m,n) would be treated as foo(i,j,[k,l,m,n]).
Each occurrence of the REPEATABLE parameter in the macro body is replaced by the bracketed text. Thus, an occurrence of v in foo's body expands to k,l,m,n.
Usually, however, it is desired to deal with one at a time of the arguments passed to the REPEATABLE parameter, not with all of them at once.
Three special-purpose macros are provided for accessing the individual arguments of a REPEATABLE parameter:
$sArg is useful when the text of a macro is to be used as if it were a STRING constant. With a normal parameter, the effect can be obtained as follows:
DEFINE print(a) = [ttyWrite("a" & eol)];
Thus:
print(Hello)
causes Hello<eol> to be written. If the parameter a were made REPEATABLE with the same macro body:
DEFINE print(REPEATABLE a) = [ttyWrite("a" & eol)];
the effect of print(x,y,z) is not the same as print(x); print(y); print(z) since the former writes x,y,z<eol> while the latter writes x<eol>y<eol>z<eol>. The following:
DEFINE print(REPEATABLE a) = [ttyWrite("$arg(a,i)" & eol)];
writes $arg(x,y,z,i)<eol>, since $arg is not recognized inside a STRING constant. Use $sArg to get the desired behavior for print:
DEFINE print(REPEATABLE a) =
[$FORC i = 1 UPTO $numArgs(a) $DOC
ttyWrite($sArg(a,i) & eol) ENDC];
In this form, print(x,y,z) writes x<eol>y<eol>z<eol>.
In fact, $numArgs, $arg, and $sArg are implemented in a way that allows them to be used with non-REPEATABLE parameters:
A REPEATABLE macro argument is really just a convenience so the programmer does not have to enclose the repeated arguments in brackets. This allows the syntax of a macro to look like that of a PROCEDURE, so that the programmer need not know whether a macro or a PROCEDURE is really being called. Defining foo as:
DEFINE foo(a,b,c) = [...];
and then invoking it as:
foo(i,j,[k,l,m,n])
has the same effect as the above example using a REPEATABLE parameter and no brackets in the call. In particular, $numArgs, $arg, and $sArg can be used to “look inside” any bracketed arguments, not just those declared REPEATABLE.
For example:
MODULE xProcs (
DEFINE
defProc(t,p) = [t PROCEDURE p] & [Proc (t parm);],
defProcX(REPEATABLE xx) = [defProc($arg(xx,1),$arg(xx,2))];
$FORC j = 1 UPTO 4 $DOC
REDEFINE
x = $arg("INTEGER,i","LONG INTEGER,li","REAL,r",
"LONG REAL,lr",j);
defProcX($expandExpr([] & x))
ENDC
);
expands to:
MODULE xProcs (
INTEGER PROCEDURE iProc (INTEGER parm);
LONG INTEGER PROCEDURE liProc (LONG INTEGER parm);
REAL PROCEDURE rProc (REAL parm);
LONG REAL PROCEDURE lrProc (LONG REAL parm);
);
($expandExpr is described in Section 15.9).
Using a parameter a in STRING quotes ("a") fails if a's argument contains undoubled STRING quotes; $sArg provides a safe, general way to convert an ordinary macro argument into a STRING constant:
DEFINE foo(a,b) = [...write(f,$sArg([a]))...];
is like:
DEFINE foo(a,b) = [...write(f,"a")...];
except that the latter is incorrect if the argument to a contains
undoubled STRING quotes.
The argument a was enclosed in brackets when
used as the argument to $sArg
because a alone could expand to what appears to be several macro
arguments.
15.7. Determining Whether a Macro Argument Has Been Omitted
A bracketed macro parameter identifier used in the governing
expression of an IFC
can be used within
a macro definition to determine whether or not the macro
parameter is empty (i.e., has been omitted),
assuming that the argument passed for the parameter is
syntactically correct (if it is not omitted).
For example,
consider a macro designed to assert a condition
that issues a message if the condition is FALSE.
It has a default message but allows that message to
be overridden.
The macro could be defined as:
DEFINE assert(condition,msg) = [
BEGIN
IF NOT (condition) THEN
IFC [msg] THENC errMsg(msg)
ELSEC errMsg("Assertion failed") ENDC END];
in which case the calls:
assert(x = y);
assert(i > 20,"i is too small")
expand to:
BEGIN IF NOT (x = y) THEN errMsg("Assertion failed") END;
BEGIN IF NOT (i > 20) THEN errMsg("i is too small") END
assert may not work if msg contains, e.g., unmatched [ or ], since the parameters are expanded before the macro bodies are parsed.
A more sophisticated version of this macro can be written using $sArg to allow conditions that contain the double quote character to be used as part of the error message and conditional compilation to ensure that extra code is generated only if the DEBUG compiler option is set:
DEFINE assert(condition,msg) = [
IFC $compileTimeValue("DEBUG") THENC
BEGIN
IF NOT (condition) THEN
IFC [msg] THENC
errMsg(msg,"",fatal);
ELSEC
errMsg("Assertion failed: "
& $sArg([condition]),
"",fatal);
ENDC
END
ENDC];
15.7.1. Omitted Macro Arguments and Empty Macro Arguments
There is currently no way to distinguish between an omitted macro
argument and an empty macro argument; both are treated like the
empty argument [].
Thus, if you define a macro like this:
DEFINE
m1(a) = [MESSAGE IFC [a] THENC "yes" ELSEC "no" ENDC];
then both these calls:
m1();
m1;
print no.
The special-purpose macro $numArgs returns 0 if its argument list is omitted entirely:
$numArgs
but 1 if it has a single empty argument:
$numArgs()
so that this macro can never return 0:
DEFINE myNumArgs(args) = [$numArgs(args)];
In the following code:
DEFINE
m1(a,b) = [IFC NOT [a] OR NOT [b] THENC
MESSAGE "Missing argument"; ENDC
... ],
m2(a,b) = [IFC [a] = [b] THENC ... ELSEC ... ENDC],
m3(a,b) = [IFC [a] = "b" THENC ... ELSEC ... ENDC];
the use of "b" in the definition of m3 instead of [b] fails if the argument b contains unpaired double quotes (e.g., if it is or contains a STRING constant, although it could be repaired with $sArg as described above). The use of [b] in m2 fails if b is a source text fragment containing unmatched square brackets (whereas "b" would work in that case if the argument contained no unpaired double quotes).
As an example of the concatenation of bracketed text and STRING constants, the code:
DEFINE a = [id] & "1";
has the same effect as:
DEFINE a = [id1];
(unless the definition occurs within a macro body, and id is a macro parameter for which text is substituted; the substitution is made in the first case but not the second). This capability is useful when macro bodies are built up from other macros.
15.9.1. $expandMacro
$expandMacro(m), where m is a macro
call (possibly with arguments), is
replaced with the body of m
(after substituting any arguments). If m is
not a macro call, the effect of $expandMacro is undefined.
In many contexts, $expandMacro(m) is equivalent to just m. However, when m is a macro argument, there is a difference. If $expandMacro occurs as a macro argument, it is evaluated before invoking the macro. For example, consider:
DEFINE foo(x) = [...x...], m = [bar];
foo($expandMacro(m)) passes the text bar as the argument to foo. Normally, macro arguments are passed exactly as is rather than being expanded before being passed; foo(m) would just pass the text m.
This macro-argument-expansion-before-invocation occurs even if the macro argument is of the form [$expandMacro(m)] (no text can occur between [ and $expandMacro). Such a macro argument is changed to:
[expanded m]
where expanded m is the result of expanding macro call m, which may involve arguments. The results are undefined if extra text follows the call to $expandMacro before the closing bracket, e.g., [$expandMacro(m)xxx].
For example, consider:
DEFINE
functions = [open,close,read,write],
numFunctions = $numArgs($expandMacro(functions));
If numFunctions were instead defined as $numArgs(functions), it would evaluate to 1 rather than 4 since the unexpanded text functions has the appearance of a single macro argument. Using $expandMacro causes the argument to $numArgs to be open,close,read,write, so that numFunctions is defined to be $numArgs(open,close,read,write), which is 4.
If numFunctions had been defined as $numArgs([$expandMacro(functions)]), it would have evaluated to 1, since it would appear to be defined as $numArgs([open,close,read,write]), which has a single (bracketed) argument.
As another example, to turn a bracketed macro body into a STRING constant:
DEFINE modelName = [model];
# using $sArg
MESSAGE $sArg($expandMacro(modelName));
# not using $sArg
DEFINE makeString(x) = ["x"];
MESSAGE makeString($expandMacro(modelName));
For example:
DEFINE
functions = [open,close,read,write],
numFunctions = $numArgs($expandMacro(functions));
REDEFINE $defVal = '1;
$FORC j = 1 UPTO numFunctions $DOC
$def($expandExpr([x] & $sArg($expandMacro(functions),j))) ENDC
$expandExpr causes its argument to be expanded before being passed to $def. Its argument is:
[x] & $sArg($expandMacro(functions),j)
$expandMacro(functions) expands to the text open,close,read,write, so the above expands to:
[x] & $sArg(open,close,read,write,j)
For j = 1, this expands to:
[x] & "open"
which is converted by $expandExpr to:
[xopen]
before passing the argument to $def (recall that the concatenation of bracketed text with a STRING constant results in bracketed text with the concatenated text). Thus, the $FORC body becomes $def([xopen]) for j = 1, which defines xopen to be '1. Subsequent iterations define xclose = '2, xread = '4, and xwrite = '10.
Like $expandMacro, $expandExpr is also detected as a
bracketed macro argument, provided that the call to $expandExpr is
the only thing between the brackets.
15.10. L Cannot Be Used as a Macro Parameter If Macro Body
Contains LONG Constants
Given the following macro definition:
DEFINE xyz(L) = [L + 1L];
The following macro call:
xyz(2L)
expands to:
2L + 12L
because the macro parameter L is substituted even when it appears as part of a LONG constant.
However, an E within a (LONG) REAL constant is not substituted if there is a macro parameter named E; e.g., given:
DEFINE uvw(e) = [e + 1E6L];
the call:
uvw(3.4L)
expands to:
3.4L + 1E6L