SOUL macro facility: Difference between revisions
m (→!UNDEF var) |
mNo edit summary |
||
Line 1: | Line 1: | ||
The User Language Macro Facility | The User Language Macro Facility | ||
consists of | 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. | ||
This facility | This facility is available to all <var class="product">[[Janus SOAP]]</var> (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 | The facility's macro statements must be the first to appear on a logical line | ||
Line 14: | Line 12: | ||
and they can be used to control the evaluation of macro statements. | and they can be used to control the evaluation of macro statements. | ||
Macro variables have no special format for their names, except | Macro variables have no special format for their names, except | ||
they cannot contain <var class="product"> | they cannot contain <var class="product">SOUL</var> separator characters such as blanks. | ||
Macro statements are evaluated at compile time, so they have ''no'' | Macro statements are evaluated at compile time, so they have ''no'' | ||
effect on evaluation-time table sizes or CCATEMP usage other than effects | effect on evaluation-time table sizes or CCATEMP usage other than effects | ||
resulting from compilation or non-compilation of non-macro <var class="product"> | resulting from compilation or non-compilation of non-macro <var class="product">SOUL</var> | ||
statements. | statements. | ||
In addition, macro statements cannot be used to control execution of | In addition, macro statements cannot be used to control execution of | ||
Line 56: | Line 54: | ||
In any request that contains any macro language statements, macro dummy | In any request that contains any macro language statements, macro dummy | ||
string substitution will be performed. | string substitution will be performed. | ||
<var class="product">Model 204</var> dummy strings are strings that begin with a question mark (<tt>?</tt>) | <var class="product">Model 204</var> [[Procedures#Dummy strings in procedures|dummy strings]] are strings that begin with a question mark (<tt>?</tt>) | ||
and some other | and some other | ||
special character, either another question mark or an ampersand (<tt>&</tt>). | special character, either another question mark or an ampersand (<tt>&</tt>). | ||
Line 62: | Line 60: | ||
parameter or a global variable value. | parameter or a global variable value. | ||
For example, in the following request, the | For example, in the following request, the file name in the <var>In</var> | ||
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>: | ||
Line 69: | Line 67: | ||
Note that the name <code>HENGE</code> is written completely in uppercase. | Note that the name <code>HENGE</code> is written completely in uppercase. | ||
This is because dummy string substitution is performed before <var>ToUpper</var> | This is because dummy string substitution is performed before <var>ToUpper</var> | ||
processing and <var class="product">Model 204</var> considers lowercase characters to not be part of | processing and <var class="product">Model 204</var> considers lowercase characters to not be part of dummy string names. | ||
dummy string names | |||
Macro dummy strings behave almost exactly the same way as <var>Include</var> parameters | Macro dummy strings behave almost exactly the same way as <var>Include</var> parameters | ||
Line 97: | 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. | ||
< | |||
is attempted in any program that contains no macro statements. | ===Handling ?! characters in program code=== | ||
<p> | |||
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 <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. | perhaps in a comment or a literal, no compilation errors will result. </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 123: | Line 120: | ||
no-op, since whatever variable is being undefined is guaranteed to be | no-op, since whatever variable is being undefined is guaranteed to be | ||
undefined already at the start of the program. | undefined already at the start of the program. | ||
</blockquote> | |||
In the event that you are using macro statements but want to have a <code>?!</code> pair | In the event that you are using macro statements but want to have a <code>?!</code> pair | ||
in a program, there are a few ways to get around the fact that the | in a program, there are a few ways to get around the fact that the | ||
macro | macro language facility will insist on trying to do macro dummy string | ||
substitution for such a reference. | substitution for such a reference. | ||
<ul> | <ul> | ||
Line 132: | Line 130: | ||
exclamation mark. | exclamation mark. | ||
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. | 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 144: | Line 143: | ||
</p> | </p> | ||
Another way to accomplish the same thing would be to use the <code>With</code> | Another way to accomplish the same thing would be to use the <code>With</code> | ||
concatenation operator. | concatenation operator. </li> | ||
</ul> | </ul> | ||
Line 211: | 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 216: | Line 216: | ||
The <var>!DEF</var> statement can be useful to set a flag that can later be used to | The <var>!DEF</var> statement can be useful to set a flag that can later be used to | ||
conditionally compile some <var class="product"> | conditionally compile some <var class="product">SOUL</var> code. | ||
In the following example, the <var>!DEF</var> statement | In the following example, the <var>!DEF</var> statement | ||
Line 233: | Line 233: | ||
==!ELSE== | ==!ELSE== | ||
The <var>!ELSE</var> statement compiles the <var class="product"> | The <var>!ELSE</var> statement compiles the <var class="product">SOUL</var> instructions that follow it — | ||
— | |||
if no <var>!IFDEF</var>, <var>!IFNDEF</var>, <var>!ELSEIFDEF</var>, or <var>!ELSEIFNDEF</var> statement at the same level | if no <var>!IFDEF</var>, <var>!IFNDEF</var>, <var>!ELSEIFDEF</var>, or <var>!ELSEIFNDEF</var> statement at the same level | ||
was evaluated as true. | was evaluated as true. | ||
Line 243: | Line 242: | ||
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">User Language</var> subroutine which (presumably) calculates | Otherwise, it calls a <var class="product">User Language</var> subroutine which (presumably) calculates | ||
logs in <var class="product"> | 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 291: | Line 290: | ||
==!ELSEIFNDEF var== | ==!ELSEIFNDEF var== | ||
The <var>!ELSEIFNDEF</var> statement compiles the <var class="product"> | The <var>!ELSEIFNDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
— if the indicated variable is not ''defined'', | — if the indicated variable is not ''defined'', | ||
and if no <var>!IFDEF</var>, <var>!IFNDEF</var>, | and if no <var>!IFDEF</var>, <var>!IFNDEF</var>, | ||
Line 340: | Line 339: | ||
==!IFDEF var== | ==!IFDEF var== | ||
The <var>!IFDEF</var> statement compiles the <var class="product"> | The <var>!IFDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
if the indicated variable is ''defined''. | if the indicated variable is ''defined''. | ||
Line 363: | Line 362: | ||
==!IFNDEF var== | ==!IFNDEF var== | ||
The <var>!IFNDEF</var> statement compiles the <var class="product"> | The <var>!IFNDEF</var> statement compiles the <var class="product">SOUL</var> instructions that follow it | ||
if the indicated variable is ''undefined''. | if the indicated variable is ''undefined''. | ||
Revision as of 18:03, 16 April 2014
The User Language 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 in 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 in 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 User Language 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 User Language 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