GHS BULLETIN
Introduction to Macros in GHS
(Applies to GHS/BHS)
"Macros" are found in various kinds computer programs. The "mainline
application" programs such as word processors, spread sheets and data-base
programs often provide something called a "macro" facility. "Macro" literally
means "large", and in computer jargon it generally refers to a command which
may be made up of a large number of other commands.
Most of the macro facilities found in application programs are built-in
versions of what was a common utility a few years ago: the "keyboard macro"
program. They function basically by recording keystrokes, allowing you to
collect the keystrokes as a named "macro" and "play them back" at a later time
by invoking the macro name.
However such "macro" facilities are awkward and of limited use compared to
an older tradition in which the macro is an extension of a programming
language. Most assembly languages have macros of this type as do a few higher-
level languages. Even DOS, which is itself a rudimentary language, has a macro
facility known as the "batch file". It is this type of "language-based macro"
which BHS implements; and in the following discussion, the term "macro" will be
used to mean this kind of macro.
The concept of the macro is very simple: a macro is a collection of
commands which, when the macro name is invoked, causes those commands to be
executed. This alone is a powerful thing; but its power is vastly increased
when "parameter substitution" is performed.
If you know a programming language such as FORTRAN, C, Pascal or a modern
version of BASIC, you are familiar with the concept of the "subroutine" (or
"subprogram" or "procedure", depending on the terminology of the particular
language). These all have the ability to "pass parameters" which are
referenced as symbols (usually variable names) within the subroutine. Although
you can also "pass parameters" when invoking a macro, the interpretation of the
parameters in macros is a little different.
When you define a macro, you are simply storing a collection of text. The
system does not require that it make sense at the time it is defined.
Therefore, your parameter substitution may take place anywhere within the
macro. You can even supply whole commands as parameters. This leads to an
extremely broad range of possibilities and is what makes the macro so powerful.
However, one of the liabilities of the macro, compared with the subroutine, is
that the system cannot warn you of syntax errors within the macro until it is
actually executed.
In GHS, the form of a macro definition is
MACRO name
body
/
where "name" is an arbitrary name by which the macro is thereafter referenced
and "body" is one or more lines of text. The single slash on a line by itself
marks the end of the macro definition. If a macro by the same name already
exists, it is replaced by the new definition. If "body" is missing, it is a
"null" macro and the definition has no effect except that if a macro by the
same name does exist, that macro is "erased" -- this is how you delete a
macro.
Other than storing a series of text lines, the MACRO command does nothing.
Therefore it may appear anywhere in a command stream. It is usually placed in
a Run File.
Making use of a previously-defined macro simply requires the name of the macro
preceded by a "dot" or period. Thus, if a macro named XYZ has been defined,
.XYZ
will cause the text in its "body" to executed as a series of commands.
If parameters are involved in a macro definition, they are represented by the
appearance, in the macro body, of the 2-character string "%n" where "n" is a
single digit 1 through 9. This is the same convention which is used by DOS in
batch files. For example,
MACRO MSDRAFT
DRAFT = %1 @ MS
/
defines a macro named MSDRAFT which contains one line of text. The "%1"
is interpreted as "parameter #1 goes here". It's sometimes called a "dummy"
parameter. If you execute this macro by saying
.MSDRAFT 12
then
DRAFT = 12 @ MS
is the actual command which the system will receive.
Consider another example:
MACRO LOAD
TANK=%1
TYPE=INTACT
CONTENTS=%3
LOAD=%2
/
This might be executed by
.LOAD FO1.P, .95, SW
yielding the command sequence
TANK=FO1.P
TYPE=INTACT
CONTENTS=SW
LOAD=.95
Note that even though the macro is named LOAD, the same as the name of an
intrinsic command, there is no confusion since a macro invocation is
distinguished by the leading "dot".
Also note that the dummy parameters can occur in any order within the body of
the macro definition. At execution time, the order in which the actual
parameters are given determines their identification with the %1, %2, etc.
within the macro body.
If there are more dummy parameters in the body than there are actual parameters
given at the execution, the missing parameters will be considered to be empty,
and the result of the substitution is that the extra dummy parameters are
removed. For example,
MACRO WT
TANK= WINGTK%1%2
/
.WT 3, .S yields TANK= WINGTK3.S
while
.WT 3 yields TANK= WINGTK3
A very important ability of GHS macros is that they can invoke other macros.
For example,
MACRO LDWT
.WT %1, *
TYPE= INTACT
LOAD= %2
CONTENTS= FO
/
MACRO LDWT1-3
.LDWT 1, %1
.LDWT 2, %1
.LDWT 3, %1
/
and the execution,
.LDWT1-3 .98
would put 98% fuel oil in three (pairs of) tanks.
As another example, consider the following macro:
MACRO HEADING
PAGE
\****** Condition: %1 ******\
/
This would work fine if you passed it a single word, such as
.HEADING DEPARTURE
but what if you wanted to use two or more words? If you threw some extra dummy
parameters into the definition (eg. "*** Condition: %1%2%3 ***"), there would
be no spaces between the words; and "*** Condition: %1 %2 %3 ***" would give
you an extra space or two if you happened to use less than three words.
The better solution is to enclose a multi-word parameter in quotation marks.
For example,
.HEADING "Wing Tanks Damaged"
would take all three words, strip off the quotation marks and then substitute
them all for %1.
Using the IF control command, you can make a macro do different things
depending on some variable or parameter. For example, you can check that the
proper number of parameters is supplied at execution time:
MACRO LOAD
IF "%3"="" THEN MESSAGE "Parameter missing! | EXIT
TANK= %1
TYPE= INTACT
CONTENTS= %3
LOAD= %2
/
Another powerful feature of macros is their ability to create or "spawn" other
macros. This is often used when implementing menus in GHS. For example,
MACRO WSET
MACRO ESC
EXIT MAIN
//
MENU " Weight setup " %1
\
\A\.ADD\Add a weight item
\D\.DEL\Delete a weight item
/
spawns a little macro named ESC prior te executing the MENU command.
Notice that MACRO ESC is treated as ordinary text when WSET is defined, with
the exception that one of the slashes is removed from its terminating line.
Thus, when WSET is executed, it right away presents the system with a macro
definition.
Incidentally, when a GHS process is aborted by your pressing the Escape
key, it looks for the presence of a macro named ESC, and if it finds one it
executes ESC instead of returning to the normal input stream.
When a macro is defined within a macro as in the example above, you must be
careful to avoid unintended parameter substitution in the inner macro.
Actually, you cannot avoid parameter substitution because every %n within the
macro is replaced when the macro is used. However you can use a little trick
to get what you want anyway. For example, If the inner macro needs one
parameter, you would like it to retain a %1 after parameter substitution has
taken place. This can be done by putting %%91 in place of the %1, assuming
that the parent macro will always have less than 9 parameters. The %9 is
replaced by nothing, leaving the %1 you wanted.
Although these brief examples were selected to demonstrate various features of
GHS macros, the primary, overriding purpose in applying macros is to reduce
repetition. Whenever you find that you have to repeat a certain sequence of
commands several times, it is almost always better to construct a macro which
contains the sequence and then execute the macro. This reduces the repetition
within the text of the Run File and thereby reduces the chance of making an
input error.
While macros can be placed in any Run File (and you will find that most Run
Files are more compact and easier to check when they use macros) it is
sometimes convenient to organize general-purpose macros into separate "Library
Files" so that they may be more conveniently used in any job. In other
respects, a Library File is no different from any other Run File. When
executing the GHS program from DOS, you can indicate that you want to have it
read a given Library File by including the file name after the "/L" parameter.
For some people, the best way to learn about using macros is to simply
experiment with using them. Others learn better by studying examples.
Examples of macro Library Files can be found in the GHS distribution files
which have the file name extension ".LIB".
If you would like to see another bulletin created regarding a specific topic,
please email Creative Systems, Inc. at support@ghsport.com.
Copyright (C) 2011
Creative Systems, Inc.