.DEFINE

Define an identifier

WSupported on Windows
USupported on Unix
VSupported on OpenVMS
NSupported in Synergy .NET
.DEFINE identifier[, replacement]

or

.DEFINE macro (arg, ...) [replacement2]

Arguments

identifier

The name of the replacement identifier to be defined.

replacement

(optional) The text to replace the identifier. The comma is optional unless replacement begins with a “(”.

macro

The name of the macro to be defined.

arg

One or more replacement arguments.

replacement2

(optional) The text to replace the macro.

Discussion

The .DEFINE compilation control directive defines an identifier and optionally assigns it replacement text. You can define parameterized macros using the second form of the .DEFINE directive above.

Whenever identifier is encountered within a subsequent logical line, replacement is substituted for identifier. The specified replacement text begins with the first non-white space character after the comma (or after identifier if the comma is omitted) and ends with the last non–white space character in the logical line, excluding comments. If replacement is not specified, identifier is removed from each following logical line in which it is encountered.

Replacement can contain another replacement identifier name. If that identifier hasn’t been processed within the current replacement operation, it is also replaced. (See example E.)

Identifier is not replaced with the specified text if the identifier is encountered within an .IFDEF, .IFNDEF, or .UNDEFINE compiler directive line. (See example B.)

By default, if a .DEFINE directive that redefines a previously defined identifier is encountered, the compiler generates a “Symbol already defined” warning (SYMDEFD), and the second definition supersedes the previous one. You can change this default by using the .DEFINE warning compiler option. If you specify this option, the compiler does not generate a warning each time you redefine a symbol or .UNDEFINE something that isn’t already defined. (See Invoking the traditional compiler (dbl) for more information about this compiler option.)

Replacement identifiers remain defined until the entire file is compiled, unless the -qrelaxed:end compiler option is set. If -qrelaxed:end is set, replacement identifiers are cleared at the end of the routine, when the END statement is encountered, instead of at the end of the file.

Tip

We recommend that you uppercase all replacement identifiers defined by the .DEFINE directive so that you can easily differentiate them from fields. We also recommend that you use the .DEFINE directive to define constant values instead of hard-coding them throughout your application. The .DEFINE directive enables you to more easily and reliably develop and maintain your applications by localizing constant values.

Built-in definitions

Several system defines are built into the compiler to enable you to conditionally compile your code based on which defines are set. Since these identifiers already exist, they do not require .DEFINE. See Built-in compiler definitions and Appendix D: Built-in Defines for more information.

Parameterized macros

A parameterized macro is a macro that can insert specified objects (parameters) when it is expanded. Each arg is a replacement identifier and only has meaning within the scope of a single definition. (In other words, it must be unique within all the arguments of a given definition, but can be duplicated across definitions.)

The replacement2 content starts with the first nonblank character following the closing “)” of the arguments, and continues until the last nonblank character on the logical line.

When a parameterized macro is invoked within code, the text associated with each of the defined arguments is inserted into the .DEFINE’s replacement text. Compilation of the line containing the parameterized define then continues. Nested parameterized macros of the same name are not permitted.

Note that continuation lines within a parameterized macro definition are allowed, but each continuation line reduces to a single space in the replacement text. Thus, the replacement text for

.define a(b) x (b)

and

.define a (b)
  & x (b)

are not the same. The replacement text for the first is “x (b),” whereas it is “<space>x (b)” for the second. This generally is not a problem, except where a definition will be used as a function.

Using the above definitions, the code

var = %a(num)

generates an error using the second definition because it expands to

var = % x(num)

which is invalid syntax. (A space is not allowed between the “%” and the “x”.) This can be solved by making sure a space is valid in an expansion. For example, the second form above can be fixed by using this form:

.define a (b) x
  & (b)

because the expansion of

var = %x (num)

is valid syntax.

When invoking a macro, arg is considered to be the first nonblank character following the leading “(” or “,” up to the last nonblank character preceding the trailing “,” or “)”.

There is a special case of the above. If the first nonblank character encountered is a “<”, the replacement text begins with the first character following the “<” and continues until the last character preceding the matching “>”. In other words, the surrounding angle brackets define the text but are stripped when used. Thus,

.define abc(arg) xyz(D_STORE, arg)

when used by

abc(<1,3, "hello">)

expands to

xyz(D_STORE, 1,3, "hello")

String concatenation is supported in the replacement text by using the following rule: If an unquoted accent grave (`) immediately precedes or follows a replacement argument, it is stripped from the final text form. In a quoted string, two accents grave preceding and a single one following a replacement argument are stripped. For example:

.define BASEREG(id,cls) id = `%cls`_cid ("DEF_``cls`_REC")

when used by

BASEREG(class_id, DSPOBJ)

expands to

class_id = %DSPOBJ_cid("DEF_DSPOBJ_REC")

Macro expansions within a string work only at a single level. For example,

.define MYNAME earl
.define name(t) "My name is ''t'"
main
record
    earl, a*, "Earl"
proc
    open(1,o,"TT:")
    writes(1,name(MYNAME)   ; This will display My name is MYNAME
end
Tip

We recommend you follow these guidelines when using parameterized macros:

  • Because white space and continuations are replaced exactly as defined in the macro, don’t insert spaces or continuations before a function name in a .DEFINE definition.
  • Include a comma if you are defining a literal value. If you are defining a parameterized macro, do not include a comma.
  • When referencing a parameterized macro, do not continue a line in the middle of a macro argument. (For example, if the argument to a macro is also a function call, the entire function call should be on one line.) You can continue between arguments but not within an argument.
  • If the macro declaration contains parentheses after the name, arguments must be specified. For example, the following is invalid:
.define MACRO() expansion
  • If you want to substitute a macro within a string that is part of another macro expansion, you have to break it into expressions that do string concatenation. For example in the above example, you can define the macros like this:
.define MYNAME earl
.define name(t) "My name is "+t

Examples

A. In this example, two simple text replacement operations use the .DEFINE directive.
.define TTCHN           ,1                ;Text replacement does not
.define FLAGS           ,xcall flags      ; include these comment lines.
proc
    FLAGS(4020)                           ;This statement becomes xcall flags(4020).
    open(TTCHN, o, "tt:")                 ;This statement becomes open(1, o, "tt:").
B. In this example, TTCHN is not replaced in the .IFNDEF directive (in other words, the directive does not become .IFNDEF 1). Since TTCHN is already defined, the .DEFINE directive within the .IFNDEF conditional is not processed.
.define TTCHN           ,1
    .
    .
    .
.ifndef TTCHN
.define TTCHN           ,1              ;This statement is not processed
.endc
C. In this example, the compiler generates a “Symbol already defined” warning (SYMDEFD) on the second .DEFINE statement unless

If you ended the routine with END and specified -qrelaxed=end, the identifier ONE is undefined after the main routine is compiled, and no error occurs.

.define ONE             ,1
proc
    .
    .
    .
.end
.define ONE             ,1
subroutine sub
proc
    .
    .
    .
endsubroutine
D. This example shows how replacement text can span across more than one physical line. See Continuation lines for more about the rules for splitting alpha literals across continuation lines.
.define SYNERGEX        ,"Synergex International 
  & Corporation"
; The replacement text is "Synergex International Corporation".
E. This example shows recursive text replacement. In the WRITES statement, the first SYNERGEX is replaced with EMPLOYER, then EMPLOYER is replaced with SYNERGEX. Since SYNERGEX has already been processed once in this replacement operation, it is not be replaced again. The second SYNERGEX (in the WRITES statement) is a separate replacement operation; therefore, it is replaced just as the first SYNERGEX was replaced. “SYNERGEXSYNERGEX” is written to the terminal.
record
  synergex  ,a8         ,"SYNERGEX"
.define TTCHN           ,1
.define SYNERGEX        ,EMPLOYER
.define EMPLOYER        ,SYNERGEX
proc
    open(TTCHN, o, "tt:")
    writes(TTCHN, SYNERGEX + SYNERGEX)
end
F. In this example, the parameterized macro SUBTOTAL is used to output various subtotals following their descriptions.
.define SUBTOTAL(desc, amount) writes(pchan, "Subtotal for ''desc': "+%string(amount))
SUBTOTAL(Apples, apples_total)
SUBTOTAL(Oranges, oranges_total)
SUBTOTAL(Mangoes, mangoes_total)

Resulting in the expansion:

writes(pchan, "Subtotal for Apples: "+%string(apples_total))
writes(pchan, "Subtotal for Oranges: "+%string(oranges_total))
writes(pchan, "Subtotal for Mangoes: "+%string(mangoes_total))

Since the names of the total variables are prefixed with their descriptions, the macro can be further simplified:

.define SUBTOTAL(desc) writes(pchan,"Subtotal for ''desc': "+%string(desc'_total))
SUBTOTAL(Apples)
SUBTOTAL(Oranges)
SUBTOTAL(Mangoes)

which results in the semantically identical expansion:

writes(pchan, "Subtotal for Apples: "+%string(apples_total))
writes(pchan, "Subtotal for Oranges: "+%string(oranges_total))
writes(pchan, "Subtotal for Mangoes: "+%string(mangoes_total))