SOUL macro facility

From m204wiki
Jump to navigation Jump to search

The SOUL macro facility consists of SOUL macro statements that allow code to be conditionally compiled. This facility is available to all Janus SOAP (and other Sirius API) customers, and, as of version 7.5, to all Model 204 customers.

The facility's macro statements must be the first to appear on a logical line (a physical line and any continuation lines), and they must begin with the exclamation character (!). Many of the macro statements also refer to macro variables, which are different from %variables or global variables. Macro variables have their names and attributes stored in CCATEMP, and they can be used to control the evaluation of macro statements. Macro variables have no special format for their names, except they cannot contain SOUL separator characters such as blanks.

Macro statements are evaluated at compile time, so they have no effect on evaluation-time table sizes or CCATEMP usage other than effects resulting from compilation or non-compilation of non-macro SOUL statements. In addition, macro statements cannot be used to control execution of Model 204 commands outside the compiler.

Currently, macro variables can only have one of two attributes: defined or undefined.

All macro language clauses (such as !IF or !ELSE clauses) are implicitly terminated when the procedure in which they were started is closed. For example, if the following procedure is closed, the !IFDEF CHUCKLES clause is terminated.

PROCEDURE EXAMPLE PRINT 'In procedure EXAMPLE' !IFDEF CHUCKLES PRINT 'CHUCKLES is defined' END PROCEDURE

Macro statements are evaluated after dummy string substitution. A statement like

!DEF .?&DEBUG

is evaluated as the following statement, if the global DEBUG is set to "DEBUG":

!DEF .DEBUG

The initial statement is evaluated as the following statement, if the global DEBUG is not set.

!DEF .

Macro dummy strings

In any request that contains any macro language statements, macro dummy string substitution will be performed. Model 204 dummy strings are strings that begin with a question mark (?) and some other special character, either another question mark or an ampersand (&). These are replaced, as the lines are being read, with either an Include parameter or a global variable value.

For example, in the following request, the file name in the In clause in the record object declaration will be replaced by the value of the global variable HENGE:

%sites is object recordset in file ?&HENGE

Note that the name HENGE is written completely in uppercase. This is because dummy string substitution is performed before ToUpper processing and Model 204 considers lowercase characters to not be part of dummy string names.

Macro dummy strings behave almost exactly the same way as Include parameters and global dummy strings, with the following exceptions:

  • They are indicated by an exclamation mark following the question mark, as in ?!henge.
  • Their values come from the macro variable whose name is indicated in the dummy string.
  • If no macro variable is defined with the indicated name, an error message results.
  • Substitution is only performed during compilation (since macro variables are only available during compilation).

So, in the following example, the Recordset object declaration would be for the file AVEBURY:

!def henge avebury ... %sites is object recordset in file ?!HENGE

As with global variable dummy strings, macro variable dummy string names must be written completely in uppercase, even if the declaration of the variables used mixed case.

Handling ?! characters in program code

For backward compatibility reasons, no macro dummy string substitution is attempted in any program that contains no macro statements. Thus if a program happens to contain the ?! characters, perhaps in a comment or a literal, no compilation errors will result.

However, if a single macro statement should appear in a program, macro dummy string substitution is enabled. If any old code in the program happened to contain a ?! pair, a compilation error is likely. If the first macro statement appears after a ?! pair, a compilation error will also result, since it is assumed that the previous ?! might have been intended as a macro dummy string. There is still the chance, however, that the facility will miss a reference to an undefined macro variable because the program contains no macro statements. If this is a concern, a dummy macro statement could be placed at the start of any program that might use macro variables:

Begin !undef nothing ...

As this example illustrates, an !undef is a good macro language no-op, since whatever variable is being undefined is guaranteed to be undefined already at the start of the program.

In the event that you are using macro statements but want to have a ?! pair in a program, there are a few ways to get around the fact that the macro language facility will insist on trying to do macro dummy string substitution for such a reference.

  • If inside a comment, simply put a blank between the question mark and exclamation mark. Unfortunately, as with Include parameter and global dummy strings, macro dummy string substitution is performed on comments.
  • If inside a literal string, move the quotes to separate the question mark from the exclamation mark:

    %x = 'Qe5 ?'!

    In such a case, Model 204 treats the string as if the text from the quote to the first delimiter character (including space) is inside the quote. If spaces or other delimiters follow the ?!, the exclamation mark could simply be quoted:

    %x = 'Fischer did Qe5 ?'!', and Spassky frowned.'

    Another way to accomplish the same thing would be to use the With

    concatenation operator.

!DUPEXIT [var]

The !DUPEXIT statement conditionally closes the current procedure, and it continues compilation in the procedure (or command level statement) that INCLUDE'd the current procedure.

As such, !DUPEXIT is not a valid statement at command level. It conditionally closes the current procedure if the explicit or implicit macro variable is defined. If the macro variable is not defined, the variable is set to defined, and processing continues in the current procedure. !DUPEXIT really performs the work of two macro statements: !IFNDEF and !DEF.

!DUPEXIT can, optionally, be followed by the name of a macro variable. If the macro variable name is not specified, it defaults to the name of the current procedure. In this latter case, the length of the procedure name must be less than the setting of the LAUDIT system parameter (unless the parameter is set to 255).

!DUPEXIT can be useful for preventing one-time-only definitions (%variable or subroutine) from being performed more than once. For example, the !DUPEXIT statement in the following example prevents subroutine COMMON from being defined more than once, as long as the subroutine is only defined in procedure COMPLEX:

PROCEDURE COMPLEX !DUPEXIT SUBROUTINE COMMON(%INPUT IS FLOAT) . . . . . . END SUBROUTINE END PROCEDURE

The first time COMPLEX is INCLUDE'd, the !DUPEXIT will not close COMPLEX (because COMPLEX will be undefined), but it will then define COMPLEX. Any subsequent time that COMPLEX is INCLUDE'd, the !DUPEXIT will cause it to be closed immediately, because at that point COMPLEX will be defined.

If subroutine COMMON might be defined in other procedures, COMPLEX might be changed as follows:

PROCEDURE COMPLEX !DUPEXIT COMMON SUBROUTINE COMMON(%INPUT IS FLOAT) . . . . . . END SUBROUTINE END PROCEDURE

This tactic will only work as long as other procedures that define subroutine COMMON also:

  • Define it conditionally based on whether COMMON is defined.
  • Define COMMON if they define the subroutine.

This can be done with a !DUPEXIT or with a !IFNDEF and a !DEF.

!DEF var

The !DEF statement sets the indicated macro variable to defined. If the variable is already defined, it remains defined.

The !DEF statement can be useful to set a flag that can later be used to conditionally compile some SOUL code.

In the following example, the !DEF statement defines a macro variable that is later used to conditionally compile some debugging statements:

!DEF DEBUGGING . . . . . !IFDEF DEBUGGING PRINT '%X = ' %X PRINT '%Y = ' %Y PRINT '%Z = ' %Z !ENDIF

!ELSE

The !ELSE statement compiles the SOUL instructions that follow it — if no !IFDEF, !IFNDEF, !ELSEIFDEF, or !ELSEIFNDEF statement at the same level was evaluated as true.

The !ELSE clause is terminated by an !ENDIF statement.

The subroutine below compiles calls to a $LOG function if the $LOG macro variable is defined. Otherwise, it calls a SOUL subroutine which (presumably) calculates logs in SOUL:

SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) %X IS FLOAT %Y IS FLOAT !IFDEF $LOG %X = $LOG(%MIN) %Y = $LOG(%MAX) !ELSE CALL ULLOG(%MIN, %X) CALL ULLOG(%MAX, %Y) !ENDIF

!ELSEIFDEF var

The !ELSEIFDEF statement compiles the SOUL instructions that follow it — if the indicated variable is defined, and if no !IFDEF, !IFNDEF, !ELSEIFDEF, or !ELSEIFNDEF at the same level was evaluated as true.

The !ELSEIFDEF clause is terminated by an !ELSE, !ELSEIFDEF, !ELSEIFNDEF, or !ENDIF statement.

If the variable specified in the !ELSEIFDEF statement is defined, and no previous !IFDEF, !IFNDEF, !ELSEIFDEF, or !ELSEIFNDEF clause has been executed, both of these result:

  • The clause that follows is compiled.
  • No code in any subsequent !ELSE, !ELSEIFDEF, or !ELSEIFNDEF clause associated with the !IFDEF statement is compiled.

The subroutine below compiles an AUDIT statement at the start of subroutine MESSY, if the DEVELOPMENT macro variable is not defined but the TEST macro variable is defined:

SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) !IFDEF DEVELOPMENT PRINT 'Entering MESSY, %MIN = ' %MIN - '%MAX = ' %MAX !ELSEIFDEF TEST AUDIT 'Entering MESSY, %MIN = ' %MIN - '%MAX = ' %MAX !ENDIF

!ELSEIFNDEF var

The !ELSEIFNDEF statement compiles the SOUL instructions that follow it — if the indicated variable is not defined, and if no !IFDEF, !IFNDEF, !ELSEIFDEF, or !ELSEIFNDEF at the same level was evaluated as true.

The !ELSEIFNDEF clause is terminated by an !ELSE, !ELSEIFDEF, !ELSEIFNDEF, or !ENDIF statement.

If the variable specified in the !ELSEIFNDEF statement is not defined, and no previous !IFDEF, !IFNDEF, !ELSEIFDEF, or !ELSEIFNDEF clause has been executed, both of these result:

  • The clause that follows is compiled.
  • No code in any subsequent !ELSE, !ELSEIFDEF, or !ELSEIFNDEF clause associated with the !IFDEF statement is compiled.

The subroutine below compiles an AUDIT statement at the start of subroutine MESSY, if the DEVELOPMENT macro variable is not defined and the PRODUCTION macro variable is not defined.

SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) !IFDEF DEVELOPMENT PRINT 'Entering MESSY, %MIN = ' %MIN - '%MAX = ' %MAX !ELSEIFNDEF PRODUCTION AUDIT 'Entering MESSY, %MIN = ' %MIN - '%MAX = ' %MAX !ENDIF

!ENDIF

The !ENDIF statement marks the end of a !IFDEF, !IFNDEF, !ELSEIFDEF, !ELSEIFNDEF, or !ELSE clause and closes out the current !IF level.

The end of a procedure implies an !ENDIF statement for all !IF levels started inside that procedure.

In the following example, the !ENDIF marks the end of debugging code, and statements after the !ENDIF will be compiled.

!IFDEF DEBUG PRINT 'Return code from $COMMNDL = ' %RC . . . . . . . !ENDIF

!IFDEF var

The !IFDEF statement compiles the SOUL instructions that follow it if the indicated variable is defined.

The !IFDEF clause is terminated by an !ELSE, !ELSEIFDEF, !ELSEIFNDEF, or !ENDIF statement.

If the variable specified in the !IFDEF statement is defined, the clause that follows is compiled, and no code in any subsequent !ELSE, !ELSEIFDEF, or !ELSEIFNDEF clause associated with the !IFDEF statement will be compiled.

The !IFDEF statement below compiles an AUDIT statement at the start of subroutine MESSY, if the DEVELOPMENT macro variable is defined:

SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) !IFDEF DEVELOPMENT AUDIT 'Entering MESSY, %MIN = ' %MIN - '%MAX = ' %MAX !ENDIF

!IFNDEF var

The !IFNDEF statement compiles the SOUL instructions that follow it if the indicated variable is undefined.

The !IFNDEF clause is terminated by an !ELSE, !ELSEIFDEF, !ELSEIFNDEF, or !ENDIF statement.

If the variable specified in the !IFNDEF statement is undefined, the clause that follows it is compiled, and no code in any subsequent !ELSE, !ELSEIFDEF, or !ELSEIFNDEF clause associated with the !IFNDEF statement will be compiled.

The !IFNDEF statement below compiles an AUDIT statement at the start of subroutine MESSY, if the PRODUCTION macro variable is undefined:

SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) !IFNDEF PRODUCTION AUDIT 'Entering MESSY, %MIN = ' %MIN - '%MAX = ' %MAX !ENDIF

!UNDEF var

The !UNDEF statement sets the indicated macro variable to undefined. If the variable is already undefined, it remains undefined.

The !UNDEF statement can be useful to clear a flag that might have been set earlier. For example, the !UNDEF statement below undefines a macro variable being used as a debugging flag, so only some of the debugging PRINT statements in the program are compiled.

!DEF DEBUGGING . . . . . !IFDEF DEBUGGING PRINT '%X = ' %X PRINT '%Y = ' %Y PRINT '%Z = ' %Z !ENDIF . . . . . !UNDEF DEBUGGING