PROGRAMMED COMMANDS
This section describes facilities for constructing more powerful
commands from simple commands. So far, the only form of compound
command structure introduced has been the facility to type more than one
command in a single command line, with the consequential capability of
repeating the complete sequence by subsequently typing a repetition
number instead of another command.
Caution
The structuring facilities described in the following paragraphs can
be used to carry out some quite complicated operations, but commands
making heavy use of them tend to become rather difficult to understand.
It is not sensible to try to devise a programmed command of any
significant complexity while working at the terminal. Contemplation in
tranquillity is required. Some users note in a log-book those which
they have found useful in the past.
Command sequences
Any sequence of commands may be enclosed in parentheses and treated
as a single command. In particular this permits a repetition count to
be attached to a sequence of commands. For example, the command
"(F.integer.I.%.)3" has the effect of inserting a percent sign in front
of the next three occurrences of "integer". Compare "F.integer. i.%.3"
which inserts three percent signs in front of the first occurrence of
"integer" only, and "F.integer.3 i.%.3" which does the same in front of
the third occurrence only. For this simple case, it would be almost as
convenient to type the pair of commands as one command line, and then
type a 2 to repeat it twice more, but that option would not cover cases
where the bracketed sequence is just part of a complete command line.
A command sequence fails when any component of it fails, so that an
asterisk attached to a bracketed command sequence specifies indefinite
repetition until one of the contained commands fails. As a check
against infinite looping, a limit of 10,000 iterations is applied to
the repetition of bracketed commands.
Alternative command sequences
Another form of compound command is one providing a number of
alternatives. This consists of two or more individual commands or
command sequences separated by commas. Execution starts with the first
command and if that alternative is completed without failure, the other
alternatives are ignored. If any of the commands making up the first
alternative fails, then the second alternative (following the first
comma) is executed, and so forth. Only if a failure occurs on the last
alternative is the whole compound command considered to have failed.
For example, it might be required to include, as a component of a more
complex command, an instruction to position the pointer at the next
space on the line, or at the end of the line if there is no space to
the right of the pointer. The command "(F1/ /, R*)" could be used for
the purpose.
Consider also the problem of interchanging two text strings, say
"basically" and "actually", throughout a complete file. Obviously, the
commands
(F/basically/S/actually/)*
M-*
(F/actually/S/basically/)*
would end up with all occurrences of both words converted to
"basically". One solution would be to convert all occurrences of the
word "basically" to some unique sequence of characters, make the other
change through the file, and finally convert the unique character
sequences to "actually". A preferable approach, using alternative
sequences, permits this kind of edit to be made progressively on a
single pass through the file.
First consider the sequence "(R,M)". Right-shift fails only when the
pointer is at the end of a line, so that this sequence performs a
Right-shift except at the end of a line, when it performs a Move. The
Move, and hence the compound command, fails only at the end of the
file. Accordingly this is a command which makes it possible to 'inch'
through a file on a character by character basis. The case under
consideration can be handled by expanding this sequence to include
alternatives to test for the two text strings and make the necessary
change, leading to:
(V/basically/S/actually/, V/actually/S/basically/, R, M)*
This framework, using a set of Verify commands together with the
'inching' sequence "R,M", is one that can be used for a variety of
different requirements.
Inverted failure condition
If a command, simple or bracketed, is followed by the symbol '\',
the failure condition for the command is inverted so that successful
execution causes a failure and unsuccessful execution does not. This
in no way alters what the Editor attempts to do by way of carrying out
the command, which has its customary effect, if any, except that it is
deemed to have failed when it has not, and conversely. This curious
effect is sometimes useful, most obviously with the Verify command.
For example, "V.+." makes it possible (in effect) to make the following
command conditional on there being a plus-sign immediately to the right
of the pointer; "V.+.\" to make the following command conditional on
there not being a plus-sign immediately to the right of the pointer.
Cancelled failure condition
If a command, simple or bracketed, is followed by the symbol '?',
any failure condition arising in the execution of the command is
cancelled, that is, any further action is taken on the basis that the
command succeeded. As with inversion, this in no way alters the effect
of the command itself. For example, suppose it was required to insert
an ampersand in front of each line in a file which started with a
exclamation-mark. The sequence "(V/!/ I/&/ M)*" is not adequate since
the part within brackets will fail on any line not starting with an
exclamation mark and cause termination of the whole sequence. It is
only the failure of the Move which should cause failure of the
sequence. Bracketing the Verify and the Insert and appending a
question-mark achieves the desired effect:
( (V/!/ I/&/)? M)*
Further examples
The examples which follow either might be useful in themselves or
illustrate general techniques. Many such commands are not rigorous,
but depend on the originator of the document maintaining a consistency
of style in terms of such matters as the inclusion or omission of
spaces.
(a) (MR)* Find first blank line
(MR\)* Find first non-blank line
The command Right-shift fails at the end of the line. Immediately
after a Move, it will fail only if the line is blank. Hence (MR)*
causes Moves to be executed until either the end of the file or a blank
line is reached. Inverting the failure condition on R locates the
first non-blank line, but note that a final L is required if the
pointer is to end up at the start of the line.
(b) (RI. .)* E- Double-space a line
This sequence inserts a space to the right of each existing
character (including spaces) on the line. The final E- removes the
last space inserted, because trailing spaces can be a source of
confusion.
(c) (R* (L D/ /)* M)* Delete trailing spaces
This command will eliminate any trailing spaces that may have crept
into the file. On each line, the pointer is moved to the end and then
successive attempts are made to Left-shift and Delete a space.
(d) ( (RLI/ /4)? M)0 Create left margin (of 4 spaces)
The obvious command would be simply "(I/ /4M)0", but that would add
spaces to blank lines, which is undesirable. Hence the "RL" to check
that the line is not blank.
(e) F.sin.(V.sin(.,S.evil.) Replace selected occurrences
The form of this command illustrates the case where it is required
to pick out certain occurrences of a word for alteration but not
others. A programmer has inadvertently employed "sin" as a variable in
a program which also makes use of the mathematical function "sin(...)".
The command sequence locates an occurrence of "sin", then verifies that
it is followed by a left parenthesis by "V.sin(." or else changes it to
"evil".