MAINSAIL Language Manual, Chapter 16

previous   next   top   contents   index   framed top   this page unframed


16. Compiler Directives and Conditional Compilation

A compiler directive indicates which source text is to be compiled or conveys information to the compiler that is used while compiling the program.

A compiler directive may occur wherever a declaration or statement may occur (except that BEGINSCAN must be the first thing on a page), and must be terminated with a semicolon.

16.1. MESSAGE

MESSAGE is a compiler directive that writes a STRING at compiletime to a new line of logFile.

The form of a MESSAGE directive is MESSAGE c; where c is a STRING constant expression, or MESSAGE c,c2;, where c is an arbitrary STRING constant expression and c2 is one of "error" or "warning" (case is ignored). c is written to logFile when the MESSAGE directive is encountered during compilation; if c2 is present, then c is included in a warning message if c2 is "warning" or an error message if c2 is "error". For example, to give a compiletime error message if the character set is unknown:

IFC $charSet = $ascii THENC ...
$EFC $charSet = $ebcdic THENC ...
ELSEC MESSAGE "Unknown char set","error"; ENDC

16.2. SOURCEFILE

SOURCEFILE directs the compiler to compile another file as if it appeared in place of the directive.

The form of a SOURCEFILE directive is SOURCEFILE c; where c is a STRING constant expression that specifies a file name. SOURCEFILE causes the compiler to save the state of the current source file (that is, the one it is currently compiling) and then begin compiling the file named by c, as if its text had appeared in place of the directive. When compilation of the file c is complete, compilation resumes immediately following the SOURCEFILE directive.

A file that was itself obtained with SOURCEFILE may also use SOURCEFILE to get additional files; i.e., SOURCEFILEs may be nested.

A SOURCEFILE name may be read from cmdFile by means of an interactive define (see Section 15.4):

DEFINE defFile "Name of file with definitions: ";
SOURCEFILE defFile;

The SOURCEFILE directive may be used to maintain a set of declarations common to a number of MODULEs in a single file that is sourcefiled by all of them. An alternative (and often more flexible) way to maintain such declarations is by using intmods; see Chapter 13.

16.3. CHECK, NOCHECK, and CHECKING

CHECK, NOCHECK, and CHECKING govern the generation of code to check certain conditions at runtime that cannot be determined at compiletime. They are described in detail in Chapter 17.

16.4. $DIRECTIVE

The directive $DIRECTIVE permits certain compiler subcommands and other directives to be specified inside the source text for a MODULE. Its format is:

$DIRECTIVE s1,...,sn;

where the si are STRING constants that are the names of compiler subcommands (followed by arguments, if applicable). The case of the the compiler subcommands in si does not matter. The subcommands currently accepted by $DIRECTIVE are:

The $DIRECTIVE directives that are not compiler subcommands are:

HIDEPROC is described in Section 16.4.1. DATETIMECHECK and NODATETIMECHECK are described in Section 16.4.2. MAKEMODULENOTVISIBLE, MAKEMODULEVISIBLE, MAKENOTVISIBLE, MAKEVISIBLE, and OPENMODULE are described in Chapter 13. PUSHACHECK, POPACHECK, PUSHCHECK and POPCHECK are described in Chapter 17.

$DIRECTIVE directives apply only to the current MODULE, i.e., are not sticky.

The directives:

$DIRECTIVE "CHECK";

and:

$DIRECTIVE "NOCHECK";

are equivalent to:

CHECK;

and:

NOCHECK;

respectively.

The current setting of many $DIRECTIVE directives can be examined with the system PROCEDURE $compileTimeValue; see Section 32.48.

16.4.1. HIDEPROC Directive

The directive:

$DIRECTIVE "HIDEPROC";

can be given inside a PROCEDURE, in which case the PROCEDURE is not compiled debuggable, does not take part in disassemblies or statement count listings, and does not have timing code inserted.

This same directive can be put outside of a PROCEDURE, in which case it applies to all subsequent PROCEDUREs for that compilation. The companion directive NOHIDEPROC turns HIDEPROC off.

This directive is useful for well-debugged PROCEDUREs that are compiled into an intmod (or compiled as FORWARD PROCEDUREs from a source file) for which the source file is not available at a site where programmers are using the debugger to step through code that calls the PROCEDUREs. The debugger will not attempt to step into HIDEPROC PROCEDUREs, and therefore will not issue any error messages for missing source files.

You may find this directive useful, e.g., if you ship a library of PROCEDUREs as an intmod (without the source code) to customers who are developing MAINSAIL programs.

16.4.2. DATETIMECHECK/NODATETIMECHECK Compiler Directives

By default, when MAINSAIL opens a supporting intmod (because it is used by another intmod), it issues a warning message if a different revision of the supporting intmod (as indicated by the date and time of compilation of the supporting intmod) was used when the requesting intmod was created. A compiler directive can be used to suppress this warning in cases where you know that no incompatible changes have been made to the supporting intmod.

The $DIRECTIVE directives DATETIMECHECK and NODATETIMECHECK, when appearing in a supporting intmod, control whether a warning message is given for that intmod. DATETIMECHECK is the default; when it is in effect for an intmod and the intmod is used as a supporting intmod, MAINSAIL performs date/time checking. NODATETIMECHECK turns off DATETIMECHECK, causing MAINSAIL to suppress the date/time check when the intmod is opened as a supporting intmod.

NODATETIMECHECK should not be specified for a MODULE if any changes have been made to it that are incompatible with existing intmods that use it as a supporting intmod (see Section 13.9.1).

16.5. SAVEON and RESTOREFROM

SAVEON and RESTOREFROM allow symbols from an intmod to be used during a compilation; see Chapter 13.

16.6. ENCODE

The ENCODE directive is used with the Foreign Language Interface (FLI, described in Chapter 7 of the MAINSAIL Compiler User's Guide) to supply target-dependent names to be written to the generated assembly language file in place of the MAINSAIL PROCEDURE identifiers. The FLI ordinarily uses some transformation (as specified in the appropriate operating-system-specific MAINSAIL user's guide) of a MAINSAIL PROCEDURE name as the foreign PROCEDURE name. In some cases the foreign name cannot be derived from the default transformation. The ENCODE directive allows the programmer to supply an arbitrary STRING as the foreign PROCEDURE name.

The form of the ENCODE directive is:

ENCODE p1 s1, ..., pn sn;

where the pi are interface PROCEDURE identifiers and the si are STRING constants. The STRING si is used as the foreign PROCEDURE name corresponding to pi when pi is compiled with the FLI compiler.

This directive:

ENCODE streamPutRec "stream_Put$Rec";

makes streamPutRec is the MAINSAIL PROCEDURE identifier used for the foreign PROCEDURE stream_Put$Rec. The FLI code generator outputs stream_Put$Rec to the assembly file as the name (label) of the foreign PROCEDURE.

The FLI is further described in the Chapter 7 of the MAINSAIL Compiler User's Guide.

16.7. $GLOBALREDEFINE

Sometimes it is useful to carry over information from one compilation to the next (within the same compiler session). This can be accomplished with the keyword $GLOBALREDEFINE, which introduces a global macro definition. The syntax and semantics of $GLOBALREDEFINE are just like those of REDEFINE, except that the defined identifiers are entered into a global symbol table that persists from one compilation to the next in the same invocation of the compiler. A macro defined in one MODULE can be accessed in a subsequently compiled MODULE.

If an identifier has been defined in a global definition, then a DEFINE or REDEFINE of the identifier defines or redefines a non-global (local to the current MODULE or PROCEDURE) occurrence of the identifier. Subsequent references to the identifier (within the scope of the non-global definition) reference the non-global definition. If the non-global definition is local to a PROCEDURE, then after the body of that PROCEDURE the global definition is once more visible.

Compiler subcommands are available for doing global redefinitions and also removing identifiers from the global symbol table.

Compiling the following file compiles into an indefinite number of empty MODULEs named FOO1, FOO2, FOO3, etc:

IFC NOT DCL(modNumTHENC
$GLOBALREDEFINE modNum = 1;
ELSEC
$GLOBALREDEFINE modNum = modNum + 1;
ENDC

$GLOBALREDEFINE modName = "foo" & cvs(modNum);

BEGIN modName

END modName

SOURCEFILE $thisFileName;

16.8. DSP and $LDSP

DSP and $LDSP are compiletime pseudoprocedures that return the displacement to a field of a CLASS or MODULE. DSP returns the displacement as an INTEGER, and $LDSP as a LONG INTEGER.

The argument to DSP or $LDSP is a field reference, where a field reference is:

For example, given the declarations:

MODULE m (INTEGER i);
CLASS c (STRING s);
CLASS d ($RECORD(cr);

the following calls to DSP are legal (the same arguments would be permitted to $LDSP):

DSP(i)   # assuming no other identifier i in top-level scope
DSP(m.i) # same as DSP(i)
DSP(c.s)
DSP(d.r)
DSP(d.r.s)

XIDAK reserves the right to change the layout of record and data section fields in memory, so always use DSP or $LDSP if you need to determine the offset of a field, rather than hardwiring the offset.

16.9. $sizeOfField

$sizeOfField(field reference) returns a LONG INTEGER for the size of its field reference argument; the field reference argument may have the same format as arguments to DSP (see Section 16.8).

If the field reference specifies a dynamic ARRAY, the size of a POINTER is returned (since a dynamic ARRAY field is implemented as a POINTER). If the specified field is an inplace record or inplace ARRAY, the size of the entire record or ARRAY is returned.

For example, given the declaration:

CLASS cls (
    
INTEGER $INPLACEARRAY(1 TO 10) ipAry;
    
LONG INTEGER(2) ARRAY(1 TO 10) dyAry;
    
$RECORD(crec;
    
LONG INTEGER(-2) i;
    
LONG INTEGER(-4) j;
    );

then:

$sizeOfField(cls.ipAry) = cvli(10 * size(integerCode))
    # 
assuming no extra padding
$sizeOfField(cls.dyAry) = $lSize(pointerCode)
$sizeOfField(cls.rec) = $lSize(c)
$sizeOfField(cls.i) = 2

16.10. $sizeOfValue

Sometimes (for example, in a macro), you need to know the size of a variable or expression whose type is not known. $sizeOfValue(expression) returns a LONG INTEGER for the size of the type of expression. expression is parsed, but not evaluated at runtime.

For example, given the declarations:

CLASS cls (
    
INTEGER $INPLACEARRAY(1 TO 10) ipAry;
    
LONG INTEGER(2) ARRAY(1 TO 10) dyAry;
    
$RECORD(crec;
    
LONG INTEGER(-2) i;
    
LONG INTEGER(-4) j;
    );
ADDRESS(clsa;

then:

$sizeOfValue(a.ipAry) = $sizeOfField(cls.ipAry)
$sizeOfValue(a.dyAry) = $sizeOfField(cls.dyAry)
$sizeOfValue(a.rec) = $sizeOfField(cls.rec)
$sizeOfValue(a.i) = $sizeOfField(cls.i)
$sizeOfValue(a.i + a.j) = $lSize(longIntegerCode)

16.11. $LEGALNOTICE

The $LEGALNOTICE directive causes a legal notice to be put into the objmod and/or intmod for the MODULE being compiled. A legal notice is typically a legal notification such as a copyright or trade secret paragraph. The compiler directive:

$LEGALNOTICE s;

where s is any STRING constant expression, is used to specify a legal notice. Only the first $LEGALNOTICE text encountered in a MODULE's source text is put into the output file(s). $LEGALNOTICE texts from referenced intmods are not put into the object file; it must appear in the source file when it is compiled. A semicolon is required after s.

16.12. Conditional Compilation: IFC, THENC, $EFC, ELSEC, and ENDC

Conditional compilation allows the programmer to specify under what conditions indicated parts of the source file are to be compiled or ignored.

The conditional:

IFC c THENC text1 ENDC

causes the compiler to compile text1 if c (a constant expression evaluable at compiletime; see Section 2.5) is non-Zero, and to ignore text1 otherwise. text1 is any source text that is valid (e.g., statement(s), declaration(s), macro definition(s)) where the conditional appears. THENC separates the condition c from the text to be conditionally compiled, and ENDC marks the end of that text.

A sample conditional is:

IFC doDebug THENC ttyWrite("current value is ",val); ENDC

The ttyWrite ... is compiled if doDebug is non-Zero; otherwise, it is ignored.

The general conditional form is:

IFC c1 THENC text1
{
$EFC c2 THENC text2
 {
$EFC c3 THENC text3
  ...
  {
$EFC cm THENC textm}}}
{
ELSEC textn}
ENDC

The ci are constant expressions evaluable at compiletime. If c1 is non-Zero, then text1 is compiled; otherwise, if c2 is present and non-Zero, then text2 is compiled; otherwise, if c3 is present and non-Zero, then text3 is compiled, etc. If none of the ci is non-Zero, and ELSEC is present, then textn is compiled. The texti that are not compiled are ignored.

“Ignored” text is really scanned (except that macros are not expanded, and compiler directives are ignored) but not parsed, which means that basic constructs such as constants must be properly constructed, and IFCs, $EFCs, ELSECs, and ENDCs must be properly matched. Otherwise, the text need not be syntactically correct.

IFCs may be nested to any depth. That is, constructs such as the following are allowed:

IFC c1 THENC
    ...
    
IFC c2 THENC text1 ELSEC text2 ENDC
    ...
ELSEC text3 ENDC

If c1 is Zero, then text3 is compiled (the rest of the conditional is scanned, but not parsed). If c1 and c2 are both non-Zero, then text1 is compiled. If c1 is non-Zero but c2 is Zero, then text2 is compiled.

The C in IFC, $EFC, THENC, ELSEC, and ENDC stands for “conditional”.

16.13. $CASEC: Compiletime Case

$CASEC provides for compiletime case selection, and is useful as a shorthand for some forms of IFC directives. The syntax is similar to that of the CASE statement:

$CASEC x OF
    [
x1]        text1
    [
x2 TO x3]  text2
    [
x4][x5]    $BEGINC text3 ENDC
    [ ]         
text4
    
ENDC

OFB may be used in place of OF (it does not take a matching END); OFB and OF are therefore exactly equivalent in this context.

16.13.1. Selectors

The case selection expression x is a constant expression of any data type; it need not be an INTEGER as it would for a CASE statement. All the expressions used in the case selectors (x1 through x5) must be of the same data type as x.

The bracketed selectors are evaluated in the order encountered, and as soon as one occurs that matches (see Section 16.13.2) the selection expression, the corresponding text is gathered into a STRING, the remainder of the $CASEC directive (down to the terminating ENDC) is discarded, and then the gathered text is compiled. The selected text is terminated by a “bare” left bracket (see Section 16.13.3) or ENDC.

The catchall selector [] matches any selection expression. All selections after it are ignored, so it should come last (this differs from the treatment of the catchall selector in the CASE statement).

16.13.2. Selector Matching Rules

A selector of the form [a] matches x if x = a, i.e., if x and a evaluate to the same value.

A selector of the form [a TO b] matches x depending on the data type of x, a, and b (all must be the same type) according to the following rules:

Type of x, a, b [a TO b] matches x if
BOOLEAN (IF a THEN 1 EL 0) LEQ (IF x THEN 1 EL 0) LEQ (IF b THEN 1 EL 0)
(LONG) INTEGER, (LONG) REAL, STRING a LEQ x LEQ b
(LONG) BITS cvli(a) LEQ cvli(x) LEQ cvli(b)
POINTER, ADDRESS, CHARADR Always matches since the only constant for these types is Zero. $CASECs of these types are not very useful.

The above computations are carried out using the host machine characteristics; avoid $CASEC if cross-compiling with non-portable values for the case selection expression or any case selector expression.

16.13.3. Delimiters of Selected Text

The selected text (text1, text2, etc. above) is arbitrary text to be compiled. If it contains a “bare” left bracket ([ or {), i.e., not inside of a STRING constant ("...[..."), character constant ('['), or comment (#...[...<eol>), the left bracket (and optionally surrounding text) must be enclosed in a $BEGINC-ENDC pair. For example:

$CASEC i OF
    [...]       ...
    [
bitsCode]  $BEGINC b := bAry[k]; ENDC
    [...]       ...
    
ENDC

This causes the compiler not to treat the left bracket as the start of the next selector. If the $BEGINC-ENDC pair had been left out of the above example, the compiler would have gathered b := bAry as the text corresponding to [bitsCode], and considered [k] to be the next selector (with just ; as its selected text), which would probably have caused a syntax error.

The gathering of the selected text applies to the text after any macro parameters have been replaced with their arguments. If any of the text contains macro parameters which could possibly be passed an argument with a bare left bracket, the text (or at least each such macro parameter) must be enclosed in a $BEGINC-ENDC pair (it never hurts to enclose the text in a $BEGINC-ENDC pair).

16.14. $BEGINC

Matched $BEGINC-ENDC pairs are useful within the selected text governed by $CASEC. In other places, matched $BEGINC-ENDC pairs are permitted, but have no effect.

16.15. $DOC, $DONEC, $CONTINUEC, $FORC: Compiletime Iteration

16.15.1. $DOC iteratedText ENDC

Text between $DOC and its terminating ENDC is repeatedly compiled until the loop is terminated, e.g., by $DONEC. The compiler gathers the iterated text into a single STRING. You must take care to avoid putting the compiler into an infinite loop.

16.15.2. $DONEC and $CONTINUEC

$DONEC and $CONTINUEC are the compiletime analogues of the DONE and CONTINUE statements.

$DONEC may be used in a $DOC body to terminate the iterations. The compiler discards the remainder of the loop body and resumes compilation beyond the ENDC matching the terminated $DOC.

$CONTINUEC may be used in a $DOC body to continue with the next iteration. The compiler resumes compilation at the top of the loop body (immediately after the $DOC).

By default, $DONEC and $CONTINUEC apply to the innermost $DOC loop. A STRING constant name may be associated with a $DOC and used by $DONEC or $CONTINUEC within the $DOC body to terminate or continue the named loop. For example:

$DOC("outer") ...
$DONEC("outer") ...
$CONTINUEC("outer") ...
ENDC

The name must be enclosed in parentheses, and the left parenthesis must immediately follow the keyword (with no intervening text, not even white space; otherwise, the parenthesized name is considered part of the iterated text). Unlike ENDs associated with named DOs, a name cannot be associated with a matching ENDC. Specifying the null STRING is equivalent to not specifying a name, so if used with a $DONEC or $CONTINUEC, it specifies the innermost loop.

Examples:

DEFINE i = 1;
$DOC 
write(f,i); REDEFINE i = i + 1;
     
IFC i > 5 THENC $DONEC ENDC ENDC

DEFINE i = 1;
$DOC("outer")
    ...
    
$DOC write(f,i); REDEFINE i = i + 1;
        
IFC i > 5 THENC $DONEC("outer") ENDC ENDC
    ... 
ENDC

16.15.3. $FORC

In the form:

$FORC var = start UPTO/DOWNTO stop $DOC ... ENDC

var is an iteration identifier that is redefined as described below, start is a (LONG) INTEGER constant expression, and stop is a constant expression of the same type as start.

The UPTO form:

$FORC var = start UPTO stop $DOC ... ENDC

is expanded by the compiler to the equivalent form:

REDEFINE var = (start) - 1; # ($FORC expansion)
$DOC REDEFINE var = var + 1; # ($FORC expansion)
     
IFC var > stop THENC $DONEC ENDC # ($FORC expansion)
     ... 
ENDC

and the DOWNTO form:

$FORC var = start DOWNTO stop $DOC ... ENDC

to:

REDEFINE var = (start) + 1; # ($FORC expansion)
$DOC REDEFINE var = var - 1; # ($FORC expansion)
     
IFC var < stop THENC $DONEC ENDC # ($FORC expansion)
     ... 
ENDC

If start and stop are LONG INTEGERs, the compiler uses 1L in place of 1.

The expanded form is compiled in place of the original text. Any errors in the expanded form show the expanded text as if it were in the source file. The comments are included to help the user recall that the text has been created by the compiler, should the text be shown in an error message.

The first example of the previous section can be rewritten as follows:

$FORC i = 1 UPTO 5 $DOC write(f,i); ENDC

An example using $FORC, $CONTINUEC, $DONEC and nested loops:

$FORC i = 1 UPTO 2 $DOC("outer")
    
$FORC j = 1 UPTO 5 $DOC
        
IFC j DIV 2 THENC $CONTINUECENDC
        ...
        
IFC ... THENC $DONEC("outer") ENDC
        ... 
ENDC
    
ENDC

16.16. Conditional Compilation and End-of-File

DONESCAN, SKIPSCAN, and end-of-file currently cause IFC and $BEGINC compilation constructs for the current source file to be closed. If such a construct is closed due to end-of-file, an error message is generated; otherwise, it is assumed that the closing syntax is in the skipped text, but the compiler currently does not examine the skipped text to check this. ($DOC, $FORC, and $CASEC behave differently; they always gather up all text to the matching ENDC before processing any text.)

This behavior may change in future releases; you should always include a terminating ENDC for any of the conditional compilation constructs (even if the rule above implies that the ENDC will not be seen), and the ENDC should appear in the same file as the start of the construct. That means you should not do the following:

$BEGINC SOURCEFILE "file that contains an ENDC";

expecting that the ENDC in the sourcefiled file will terminate the $BEGINC.

16.17. DCL

DCL is a compiletime pseudoprocedure. DCL(identifier) is TRUE if identifier has been declared or defined (by the programmer, or as a standard MAINSAIL identifier), and FALSE otherwise. DCL is useful in conjunction with conditional compilation. For example:

IFC NOT DCL(switchTHENC DEFINE switch = FALSEENDC

If switch has been declared or defined then DEFINE switch = FALSE; is ignored; otherwise, it is compiled.

16.18. $TYPEOF

$TYPEOF(x) returns the INTEGER constant type code of the expression, CLASS name, or MODULE name x. The compiler parses x, determines the type of the result, then discards the resulting parse information (x is not actually evaluated). The type codes that can be returned are the following:

booleanCode     integerCode     longIntegerCode realCode
longRealCode    bitsCode        longBitsCode    stringCode
addressCode     charadrCode     pointerCode     $recordCode
$procvarCode    $classCode      $moduleCode

Temporary feature: subject to change

$typeOf currently returns the type code of the base type of its argument expression, not an extended type code; e.g., $typeOf(i1), where i1 is an INTEGER(1), is integerCode, not $integer1Code.

This behavior is subject to change; future versions of $typeOf may return the extended type code for explicitly sized data types.

If x is an ARRAY (inplace or dynamic), $TYPEOF(x) is the base type of the ARRAY, i.e., the type of the elements (0 if untyped). If x is an inplace record, $TYPEOF returns $recordCode. If x is a CLASS or MODULE name, $TYPEOF returns $classCode or $moduleCode, respectively.

Given the declarations:

CLASS cls (STRING sPOINTER(clink);
MODULE          m;
INTEGER         t,u;
POINTER(cls)    p;

$TYPEOF returns the type codes shown for the following expressions:

Expression Result
$TYPEOF(t) integerCode
$TYPEOF(t + u) integerCode
$TYPEOF(p) pointerCode
$TYPEOF(p.s) stringCode
$TYPEOF(p.link) pointerCode
$TYPEOF(sin(1.)) realCode
$TYPEOF(cls) $classCode
$TYPEOF(m) $moduleCode

An example of a macro that can increment either an INTEGER or LONG INTEGER:

DEFINE inc(a) =
        [
a .+ IFC $TYPEOF(a) = integerCode THENC 1
              
ELSEC 1L ENDC];

16.19. $CLASSOF

$CLASSOF(x) returns the STRING constant CLASS name (upper case) for the expression x if x evaluates to a classified POINTER or ADDRESS; otherwise, it returns the null STRING. The compiler parses x as an expression, determines the CLASS name of the result (if a classified POINTER or ADDRESS), then discards the resulting parse information (x is not actually evaluated).

For example, given the declarations:

CLASS cls (STRING sPOINTER(clink);
MODULE          m;
INTEGER         t,u;
POINTER(cls)    p;

$CLASSOF returns the values shown for the following expressions:

Expression Result
$CLASSOF(t) "" (null STRING)
$CLASSOF(t + u) ""
$CLASSOF(p) "CLS"
$CLASSOF(p.s) ""
$CLASSOF(p.link) "C"
$CLASSOF(sin(1.)) ""

Given the declarations:

CLASS rec1 (...; POINTER(rec1nextRec1);
CLASS rec2 (...; POINTER(rec2nextRec2);

you can define a macro that depends on the class of its POINTER argument:

DEFINE nextRec(p) = # make it work for rec1 and rec2
        [
p := IFC $CLASSOF(p) = "REC1THENC p.nextRec1
              
ELSEC p.nextRec2 ENDC];

16.20. $ISCONSTANT

$ISCONSTANT(x) returns TRUE if and only if the expression x evaluates to a constant. The compiler parses x as an expression, determines whether it evaluates to a constant, then discards the resulting parse information (x is not actually evaluated).

For example, suppose calls to the local PROCEDURE fooProc, which takes one argument, are desired to be inline if the argument is a constant. Define foo as below, then use foo instead of fooProc:

DEFINE foo(x) =
    [
IFC $ISCONSTANT(xTHENC INLINE ENDC
        
fooProc(xENDC];

16.21. Scanning Directives

The scanning directives are BEGINSCAN, SKIPSCAN, and DONESCAN.

SKIPSCAN allows the compiler to skip quickly over pages in the source file. The form of a SKIPSCAN directive is:

SKIPSCAN c;

where c is a STRING constant scan name. This directive causes the compiler to begin skipping pages until it finds one that starts with the keyword BEGINSCAN followed by the same scan name. Compilation then resumes on that page. Pages are delimited by eop characters (see Section 2.1). Upper and lower case are not distinguished in examining the scan name.

If c is Zero, i.e., if:

SKIPSCAN "";

is encountered, the compiler stops at the next BEGINSCAN c, regardless of the value of c.

Macros are not expanded within text skipped over by SKIPSCAN, and compiler directives (other than a matching BEGINSCAN) are ignored.

BEGINSCAN serves as a stopping point for a SKIPSCAN search.

The form of a BEGINSCAN directive is:

BEGINSCAN c;

where c is a STRING constant scan name. It is used by the SKIPSCAN search to determine whether or not the search should stop at this BEGINSCAN.

BEGINSCAN must appear as the very first text on a page, not even preceded by blank or tab; this allows the SKIPSCAN search to be fast since it need examine only the first line of each page. The compiler ignores the BEGINSCAN directive except during a SKIPSCAN search.

If c is Zero, the BEGINSCAN directive stops any SKIPSCAN search; i.e.:

BEGINSCAN "";

stops any SKIPSCAN search.

DONESCAN terminates compilation of the current file as if the end of the file were reached. It may be used to return from a sourcefile of a file that is being used as a repository for several sources.

The form of a DONESCAN directive is:

DONESCAN;

The scanning directives may be used to compile MODULEs selectively in a file that contains more than one MODULE. That is, proper use of the scanning directives can direct the compiler to compile just some of the MODULEs in the file, depending on which ones are specified by the user.

16.22. NEEDBODY and NEEDANYBODIES

The compiletime pseudoprocedures NEEDBODY and NEEDANYBODIES are used in conjunction with the FORWARD qualifier (see Section 7.13) to determine whether a FORWARD PROCEDURE needs a body, i.e., has been called but has not yet been given a declaration containing the PROCEDURE body. NEEDBODY may also be used to determine whether an interface PROCEDURE (whether called or not) needs a body.

The form:

NEEDBODY(id)

is TRUE if and only if id is the name of a PROCEDURE that either has been declared FORWARD and has appeared in a PROCEDURE call or is an interface PROCEDURE, but has not been declared with a body.

NEEDANYBODIES has two forms, one followed by a parenthesized file name, and one not. The form:

NEEDANYBODIES(c)

where c is a STRING constant expression for a file name, is TRUE if and only if some PROCEDURE p was declared with the qualifier FORWARD(c), and NEEDBODY(p) is currently TRUE. In other words, NEEDANYBODIES(c) tells whether there are any PROCEDURE bodies in the file c that need to be compiled.

The form:

NEEDANYBODIES

is equivalent to:

NEEDANYBODIES(c)

where c is the name of the file that caused the current automatic SOURCEFILE as explained in Section 7.13. The implied c is the “top-level” file that was sourcefiled, not necessarily the current file (additional SOURCEFILE directives may occur in the top-level file).

The $compileTimeValue argument HASBODY may be used to determine whether a PROCEDURE has been given a body, regardless of how the PROCEDURE was declared.

16.23. $compileTimeValue

$compileTimeValue is a compiletime PROCEDURE that provides a number of miscellaneous compiletime facilities. It is described in detail in Section 32.48.

16.24. $def

The macro $def is an aid to defining a consecutive sequence of (LONG) INTEGERs, or a sequence of (LONG) BITS each shifted one more bit to the left than the previous value. $def(a,v) redefines a to be v, and then:

The user must define v's starting value before using $def.

Example:

define a = 1, b = 2, c = 3, d = 4

DEFINE v = 1;
$def(a,v)
$def(b,v)
$def(c,v)
$def(d,v)

The second argument, v, may be omitted, in which case the identifier $defVal is used in place of v. $defVal is an identifier which has been set aside for this purpose. In this case, the user must initialize $defVal with REDEFINE since it may already be defined at the start of compilation. It is expected that most uses of $def will omit the second argument and use $defVal; the second argument would be useful, however, if multiple sequences need to be defined in parallel.

Example:

define a = '1, b = '2, c = '4, d = '10

REDEFINE $defVal = '1;
$def(a)
$def(b)
$def(c)
$def(d)

REDEFINE allows the type of a macro to be changed; for example, if $defVal is defined to be an INTEGER constant, then:

REDEFINE $defVal = '1L;

changes it to be a LONG BITS constant.

There is nothing “magic” about $def; i.e., it could just as easily be provided by the user. $def is defined as follows:

DEFINE
    
$def(a,v) =
        [
IFC [vTHENC
            
REDEFINE
                
a = v,
                
v = IFC $TYPEOF(v) = integerCode THENC v + 1
                    
$EFC $TYPEOF(v) = longIntegerCode THENC
                              
v + 1L
                    
ELSEC v SHL 1 ENDC;
         
ELSEC
            
REDEFINE
                
a = $defVal,
                
$defVal =
                    
IFC $TYPEOF($defVal) = integerCode THENC
                        
$defVal + 1
                    
$EFC $TYPEOF($defVal) = longIntegerCode
                          
THENC $defVal + 1L
                    
ELSEC $defVal SHL 1 ENDC;
         
ENDC];

The exact definition of $def is subject to change; the above is presented only as an example.


previous   next   top   contents   index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 16