SOUL macro facility: Difference between revisions
m (Dme moved page User Language Macro Facility to SOUL macro facility) |
|||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
The | The SOUL macro facility | ||
consists of <var class="product">SOUL</var> | consists of <var class="product">SOUL</var> | ||
'''macro statements''' that allow code to be conditionally compiled. | '''macro statements''' that allow code to be conditionally compiled. | ||
Line 50: | Line 50: | ||
<p class="code">!DEF . | <p class="code">!DEF . | ||
</p> | </p> | ||
==Macro dummy strings== | ==Macro dummy strings== | ||
In any request that contains any macro language statements, macro dummy | In any request that contains any macro language statements, macro dummy | ||
Line 63: | Line 63: | ||
clause in the record object declaration will be replaced by the value of | clause in the record object declaration will be replaced by the value of | ||
the global variable <code>HENGE</code>: | the global variable <code>HENGE</code>: | ||
<p class="code">%sites | <p class="code">%sites is object recordset in file ?&HENGE | ||
</p> | </p> | ||
Note that the name <code>HENGE</code> is written completely in uppercase. | Note that the name <code>HENGE</code> is written completely in uppercase. | ||
Line 86: | Line 86: | ||
<p class="code">!def henge avebury | <p class="code">!def henge avebury | ||
... | ... | ||
%sites | %sites is object recordset in file ?!HENGE | ||
</p> | </p> | ||
Line 92: | Line 92: | ||
be written completely in uppercase, even if the declaration of the variables | be written completely in uppercase, even if the declaration of the variables | ||
used mixed case. | used mixed case. | ||
===Handling ?! characters in program code=== | ===Handling ?! characters in program code=== | ||
<p> | <p> | ||
Line 98: | Line 98: | ||
Thus if a program happens to contain the <code>?!</code> characters, | Thus if a program happens to contain the <code>?!</code> characters, | ||
perhaps in a comment or a literal, no compilation errors will result. </p> | perhaps in a comment or a literal, no compilation errors will result. </p> | ||
<p> | <p> | ||
However, if a single macro statement should appear in a program, macro dummy | However, if a single macro statement should appear in a program, macro dummy | ||
string substitution is enabled. | string substitution is enabled. | ||
Line 131: | Line 131: | ||
Unfortunately, as with <var>Include</var> parameter and global dummy strings, macro dummy | Unfortunately, as with <var>Include</var> parameter and global dummy strings, macro dummy | ||
string substitution is performed on comments. </li> | string substitution is performed on comments. </li> | ||
<li>If inside a literal string, move the quotes to separate the question mark | <li>If inside a literal string, move the quotes to separate the question mark | ||
from the exclamation mark: | from the exclamation mark: | ||
Line 210: | Line 210: | ||
</ul> | </ul> | ||
This can be done with a <var>!DUPEXIT</var> or with a <var>!IFNDEF</var> and a <var>!DEF</var>. | This can be done with a <var>!DUPEXIT</var> or with a <var>!IFNDEF</var> and a <var>!DEF</var>. | ||
==!DEF var== | ==!DEF var== | ||
The <var>!DEF</var> statement sets the indicated macro variable to ''defined''. | The <var>!DEF</var> statement sets the indicated macro variable to ''defined''. | ||
Line 231: | Line 231: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
==!ELSE== | ==!ELSE== | ||
The <var>!ELSE</var> statement compiles the <var class="product">SOUL</var> instructions that follow it — | The <var>!ELSE</var> statement compiles the <var class="product">SOUL</var> instructions that follow it — | ||
Line 241: | Line 241: | ||
The subroutine below | The subroutine below | ||
compiles calls to a <var>$LOG</var> function if the <var>$LOG</var> macro variable is defined. | compiles calls to a <var>$LOG</var> function if the <var>$LOG</var> macro variable is defined. | ||
Otherwise, it calls a <var class="product"> | Otherwise, it calls a <var class="product">SOUL</var> subroutine which (presumably) calculates | ||
logs in <var class="product">SOUL</var>: | logs in <var class="product">SOUL</var>: | ||
<p class="code">SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) | <p class="code">SUBROUTINE MESSY(%MIN IS FLOAT, %MAX IS FLOAT) | ||
Line 256: | Line 256: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
==!ELSEIFDEF var== | ==!ELSEIFDEF var== | ||
The <var>!ELSEIFDEF</var> statement compiles the <var class="product"> | The <var>!ELSEIFDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
— if the indicated variable is ''defined'', and if no <var>!IFDEF</var>, <var>!IFNDEF</var>, | — if the indicated variable is ''defined'', and if no <var>!IFDEF</var>, <var>!IFNDEF</var>, | ||
<var>!ELSEIFDEF</var>, or <var>!ELSEIFNDEF</var> at the same level was evaluated as true. | <var>!ELSEIFDEF</var>, or <var>!ELSEIFNDEF</var> at the same level was evaluated as true. | ||
Line 288: | Line 288: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
==!ELSEIFNDEF var== | ==!ELSEIFNDEF var== | ||
The <var>!ELSEIFNDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | The <var>!ELSEIFNDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
Line 321: | Line 321: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
==!ENDIF== | ==!ENDIF== | ||
The <var>!ENDIF</var> statement marks the end of a <var>!IFDEF</var>, <var>!IFNDEF</var>, <var>!ELSEIFDEF</var>, <var>!ELSEIFNDEF</var>, | The <var>!ENDIF</var> statement marks the end of a <var>!IFDEF</var>, <var>!IFNDEF</var>, <var>!ELSEIFDEF</var>, <var>!ELSEIFNDEF</var>, | ||
Line 337: | Line 337: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
==!IFDEF var== | ==!IFDEF var== | ||
The <var>!IFDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | The <var>!IFDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
Line 360: | Line 360: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
==!IFNDEF var== | ==!IFNDEF var== | ||
The <var>!IFNDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | The <var>!IFNDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
Line 383: | Line 383: | ||
!ENDIF | !ENDIF | ||
</p> | </p> | ||
[[Category: User Language syntax enhancements]] | [[Category: User Language syntax enhancements]] | ||
==!UNDEF var== | ==!UNDEF var== | ||
The <var>!UNDEF</var> statement sets the indicated macro variable to ''undefined''. | The <var>!UNDEF</var> statement sets the indicated macro variable to ''undefined''. | ||
Line 408: | Line 408: | ||
!UNDEF DEBUGGING | !UNDEF DEBUGGING | ||
</p> | </p> | ||
[[Category: | [[Category:SOUL macro facility]] | ||
[[Category: User Language syntax enhancements]] | [[Category: User Language syntax enhancements]] |
Latest revision as of 12:25, 16 July 2019
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
concatenation operator.With
!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